Added support for plugin features
[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 {
182 *plugin = (*entry)->plugin;
183 return TRUE;
184 }
185
186 METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*,
187 private_plugin_loader_t *this)
188 {
189 return enumerator_create_filter(
190 this->plugins->create_enumerator(this->plugins),
191 (void*)plugin_filter, NULL, NULL);
192 }
193
194 /**
195 * Check if a plugin is already loaded
196 */
197 static bool plugin_loaded(private_plugin_loader_t *this, char *name)
198 {
199 enumerator_t *enumerator;
200 bool found = FALSE;
201 plugin_t *plugin;
202
203 enumerator = create_plugin_enumerator(this);
204 while (enumerator->enumerate(enumerator, &plugin, NULL))
205 {
206 if (streq(plugin->get_name(plugin), name))
207 {
208 found = TRUE;
209 break;
210 }
211 }
212 enumerator->destroy(enumerator);
213 return found;
214 }
215
216 /**
217 * Check if a feature of a plugin is already loaded
218 */
219 static bool feature_loaded(private_plugin_loader_t *this, plugin_entry_t *entry,
220 plugin_feature_t *feature)
221 {
222 return entry->loaded->find_first(entry->loaded, NULL,
223 (void**)&feature) == SUCCESS;
224 }
225
226 /**
227 * Check if dependencies are satisfied
228 */
229
230 static bool dependencies_satisfied(private_plugin_loader_t *this, char *name,
231 bool soft, bool report, plugin_feature_t *features, int count)
232 {
233 int i;
234
235 /* first entry is provided feature, followed by dependencies */
236 for (i = 1; i < count; i++)
237 {
238 enumerator_t *entries, *loaded;
239 plugin_feature_t *feature;
240 plugin_entry_t *entry;
241 bool found = FALSE;
242
243 if (features[i].kind != FEATURE_DEPENDS &&
244 features[i].kind != FEATURE_SDEPEND)
245 { /* end of dependencies */
246 break;
247 }
248 entries = this->plugins->create_enumerator(this->plugins);
249 while (entries->enumerate(entries, &entry))
250 {
251 loaded = entry->loaded->create_enumerator(entry->loaded);
252 while (loaded->enumerate(loaded, &feature))
253 {
254 if (plugin_feature_matches(&features[i], feature))
255 {
256 found = TRUE;
257 break;
258 }
259 }
260 loaded->destroy(loaded);
261 }
262 entries->destroy(entries);
263
264 if (!found && !(features[i].kind == FEATURE_SDEPEND && !soft))
265 {
266 if (report)
267 {
268 char *provide, *depend;
269
270 provide = plugin_feature_get_string(&features[0]);
271 depend = plugin_feature_get_string(&features[i]);
272 DBG1(DBG_LIB, "feature %s in '%s' plugin has unsatisfied "
273 "dependency: %s", provide, name, depend);
274 free(provide);
275 free(depend);
276 }
277 return FALSE;
278 }
279 }
280 return TRUE;
281 }
282
283 /**
284 * Load a plugin feature
285 */
286 static bool load_feature(private_plugin_loader_t *this, plugin_entry_t *entry,
287 char *name, plugin_feature_t *feature, plugin_feature_t *reg)
288 {
289 char *str;
290
291 str = plugin_feature_get_string(feature);
292 switch (feature->type)
293 {
294 case FEATURE_CRYPTER:
295 case FEATURE_SIGNER:
296 case FEATURE_HASHER:
297 case FEATURE_PRF:
298 case FEATURE_DH:
299 case FEATURE_RNG:
300 case FEATURE_PRIVKEY:
301 case FEATURE_PRIVKEY_GEN:
302 case FEATURE_PUBKEY:
303 case FEATURE_CERT_DECODE:
304 case FEATURE_CERT_ENCODE:
305 /* require a registration function */
306 if (!reg ||
307 (reg->kind == FEATURE_REGISTER && reg->type != feature->type))
308 {
309 DBG1(DBG_LIB, "loading '%s' plugin feature %s failed: "
310 "invalid registration function", name, str);
311 free(str);
312 return FALSE;
313 }
314 break;
315 default:
316 break;
317 }
318 if (reg && reg->kind == FEATURE_CALLBACK)
319 {
320 if (!reg->cb.f(entry->plugin, feature, TRUE, reg->cb.data))
321 {
322 DBG1(DBG_LIB, "loading '%s' plugin feature %s with callback failed",
323 name, str);
324 free(str);
325 return FALSE;
326 }
327 }
328 else
329 {
330 switch (feature->type)
331 {
332 case FEATURE_CRYPTER:
333 lib->crypto->add_crypter(lib->crypto, feature->crypter.alg,
334 name, reg->reg.f);
335 break;
336 case FEATURE_SIGNER:
337 lib->crypto->add_signer(lib->crypto, feature->signer,
338 name, reg->reg.f);
339 break;
340 case FEATURE_HASHER:
341 lib->crypto->add_hasher(lib->crypto, feature->hasher,
342 name, reg->reg.f);
343 break;
344 case FEATURE_PRF:
345 lib->crypto->add_prf(lib->crypto, feature->prf,
346 name, reg->reg.f);
347 break;
348 case FEATURE_DH:
349 lib->crypto->add_dh(lib->crypto, feature->dh_group,
350 name, reg->reg.f);
351 break;
352 case FEATURE_RNG:
353 lib->crypto->add_rng(lib->crypto, feature->rng_quality,
354 name, reg->reg.f);
355 break;
356 case FEATURE_PRIVKEY:
357 case FEATURE_PRIVKEY_GEN:
358 lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY,
359 feature->privkey, reg->reg.final, reg->reg.f);
360 break;
361 case FEATURE_PUBKEY:
362 lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY,
363 feature->pubkey, reg->reg.final, reg->reg.f);
364 break;
365 case FEATURE_CERT_DECODE:
366 case FEATURE_CERT_ENCODE:
367 lib->creds->add_builder(lib->creds, CRED_CERTIFICATE,
368 feature->cert, reg->reg.final, reg->reg.f);
369 break;
370 default:
371 break;
372 }
373 }
374 DBG2(DBG_LIB, "loaded '%s' plugin feature %s", name, str);
375 free(str);
376 entry->loaded->insert_last(entry->loaded, feature);
377 return TRUE;
378 }
379
380 /**
381 * Load plugin features in correct order
382 */
383 static int load_features(private_plugin_loader_t *this, bool soft, bool report)
384 {
385 enumerator_t *enumerator;
386 plugin_feature_t *features, *reg = NULL;
387 plugin_entry_t *entry;
388 int count, i, loaded = 0;
389 char *name;
390
391 enumerator = this->plugins->create_enumerator(this->plugins);
392 while (enumerator->enumerate(enumerator, &entry))
393 {
394 if (!entry->plugin->get_features)
395 { /* feature interface not supported */
396 continue;
397 }
398 name = entry->plugin->get_name(entry->plugin);
399 count = entry->plugin->get_features(entry->plugin, &features);
400 for (i = 0; i < count; i++)
401 {
402 switch (features[i].kind)
403 {
404 case FEATURE_PROVIDE:
405 if (!feature_loaded(this, entry, &features[i]) &&
406 dependencies_satisfied(this, name, soft, report,
407 &features[i], count - i) &&
408 load_feature(this, entry, name, &features[i], reg))
409 {
410 loaded++;
411 }
412 break;
413 case FEATURE_REGISTER:
414 case FEATURE_CALLBACK:
415 reg = &features[i];
416 break;
417 default:
418 break;
419 }
420 }
421 }
422 enumerator->destroy(enumerator);
423 return loaded;
424 }
425
426 METHOD(plugin_loader_t, load_plugins, bool,
427 private_plugin_loader_t *this, char *path, char *list)
428 {
429 enumerator_t *enumerator;
430 char *token;
431 bool critical_failed = FALSE;
432
433 if (path == NULL)
434 {
435 path = PLUGINDIR;
436 }
437
438 enumerator = enumerator_create_token(list, " ", " ");
439 while (!critical_failed && enumerator->enumerate(enumerator, &token))
440 {
441 bool critical = FALSE;
442 char file[PATH_MAX];
443 int len;
444
445 token = strdup(token);
446 len = strlen(token);
447 if (token[len-1] == '!')
448 {
449 critical = TRUE;
450 token[len-1] = '\0';
451 }
452 if (plugin_loaded(this, token))
453 {
454 free(token);
455 continue;
456 }
457 if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so",
458 path, token) >= sizeof(file))
459 {
460 return NULL;
461 }
462 if (!load_plugin(this, token, file) && critical)
463 {
464 critical_failed = TRUE;
465 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
466 }
467 free(token);
468 }
469 enumerator->destroy(enumerator);
470 if (!critical_failed)
471 {
472 while (load_features(this, TRUE, FALSE))
473 {
474 /* try load new features until we don't get new ones */
475 }
476 while (load_features(this, FALSE, FALSE))
477 {
478 /* second round, ignoring soft dependencies */
479 }
480 /* report missing dependencies */
481 load_features(this, FALSE, TRUE);
482 }
483 return !critical_failed;
484 }
485
486 METHOD(plugin_loader_t, unload, void,
487 private_plugin_loader_t *this)
488 {
489 plugin_entry_t *entry;
490
491 /* unload plugins in reverse order */
492 while (this->plugins->remove_last(this->plugins,
493 (void**)&entry) == SUCCESS)
494 {
495 if (lib->leak_detective)
496 { /* keep handle to report leaks properly */
497 entry->handle = NULL;
498 }
499 plugin_entry_destroy(entry);
500 }
501 }
502
503 /**
504 * Reload a plugin by name, NULL for all
505 */
506 static u_int reload_by_name(private_plugin_loader_t *this, char *name)
507 {
508 u_int reloaded = 0;
509 enumerator_t *enumerator;
510 plugin_t *plugin;
511
512 enumerator = create_plugin_enumerator(this);
513 while (enumerator->enumerate(enumerator, &plugin, NULL))
514 {
515 if (name == NULL || streq(name, plugin->get_name(plugin)))
516 {
517 if (plugin->reload && plugin->reload(plugin))
518 {
519 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
520 plugin->get_name(plugin));
521 reloaded++;
522 }
523 }
524 }
525 enumerator->destroy(enumerator);
526 return reloaded;
527 }
528
529 METHOD(plugin_loader_t, reload, u_int,
530 private_plugin_loader_t *this, char *list)
531 {
532 u_int reloaded = 0;
533 enumerator_t *enumerator;
534 char *name;
535
536 if (list == NULL)
537 {
538 return reload_by_name(this, NULL);
539 }
540 enumerator = enumerator_create_token(list, " ", "");
541 while (enumerator->enumerate(enumerator, &name))
542 {
543 reloaded += reload_by_name(this, name);
544 }
545 enumerator->destroy(enumerator);
546 return reloaded;
547 }
548
549 METHOD(plugin_loader_t, destroy, void,
550 private_plugin_loader_t *this)
551 {
552 unload(this);
553 this->plugins->destroy(this->plugins);
554 free(this);
555 }
556
557 /*
558 * see header file
559 */
560 plugin_loader_t *plugin_loader_create()
561 {
562 private_plugin_loader_t *this;
563
564 INIT(this,
565 .public = {
566 .load = _load_plugins,
567 .reload = _reload,
568 .unload = _unload,
569 .create_plugin_enumerator = _create_plugin_enumerator,
570 .destroy = _destroy,
571 },
572 .plugins = linked_list_create(),
573 );
574
575 return &this->public;
576 }
577