1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "str.h"
6 #include "sort.h"
7 #include "module-dir.h"
8
9 #ifdef HAVE_MODULES
10
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <dirent.h>
14 #include <dlfcn.h>
15
16 #ifndef RTLD_GLOBAL
17 # define RTLD_GLOBAL 0
18 #endif
19
20 #ifndef RTLD_NOW
21 # define RTLD_NOW 0
22 #endif
23
24 static const char *module_name_drop_suffix(const char *name);
25
module_get_symbol_quiet(struct module * module,const char * symbol)26 void *module_get_symbol_quiet(struct module *module, const char *symbol)
27 {
28 /* clear out old errors */
29 (void)dlerror();
30
31 return dlsym(module->handle, symbol);
32 }
33
module_get_symbol(struct module * module,const char * symbol)34 void *module_get_symbol(struct module *module, const char *symbol)
35 {
36 const char *error;
37 void *ret;
38
39 ret = module_get_symbol_quiet(module, symbol);
40 if (ret == NULL) {
41 error = dlerror();
42 if (error != NULL) {
43 i_error("module %s: dlsym(%s) failed: %s",
44 module->path, symbol, error);
45 ret = NULL;
46 }
47 }
48 return ret;
49 }
50
get_symbol(struct module * module,const char * symbol,bool quiet)51 static void *get_symbol(struct module *module, const char *symbol, bool quiet)
52 {
53 if (quiet)
54 return module_get_symbol_quiet(module, symbol);
55
56 return module_get_symbol(module, symbol);
57 }
58
module_free(struct module * module)59 static void module_free(struct module *module)
60 {
61 if (module->deinit != NULL && module->initialized)
62 module->deinit();
63 /* dlclose()ing removes all symbols from valgrind's visibility.
64 if GDB environment is set, don't actually unload the module
65 (the GDB environment is used elsewhere too) */
66 if (getenv("GDB") == NULL) {
67 if (dlclose(module->handle) != 0)
68 i_error("dlclose(%s) failed: %m", module->path);
69 }
70 i_free(module->path);
71 i_free(module->name);
72 i_free(module);
73 }
74
75 static bool
module_check_wrong_binary_dependency(const struct module_dir_load_settings * set,struct module * module,const char ** error_r)76 module_check_wrong_binary_dependency(const struct module_dir_load_settings *set,
77 struct module *module, const char **error_r)
78 {
79 const char *symbol_name, *binary_dep, *const *names;
80 string_t *errstr;
81
82 if (set->binary_name == NULL)
83 return TRUE;
84
85 symbol_name = t_strconcat(module->name, "_binary_dependency", NULL);
86 binary_dep = dlsym(module->handle, symbol_name);
87 if (binary_dep == NULL)
88 return TRUE;
89
90 names = t_strsplit(binary_dep, " ");
91 if (str_array_find(names, set->binary_name))
92 return TRUE;
93
94 errstr = t_str_new(128);
95 str_printfa(errstr, "Can't load plugin %s: "
96 "Plugin is intended to be used only by ", module->name);
97 if (names[1] == NULL)
98 str_printfa(errstr, "%s binary", binary_dep);
99 else
100 str_printfa(errstr, "binaries: %s", binary_dep);
101 str_printfa(errstr, " (we're %s)", set->binary_name);
102 *error_r = str_c(errstr);
103 return FALSE;
104 }
105
106 static bool
module_check_missing_plugin_dependencies(const struct module_dir_load_settings * set,struct module * module,struct module * all_modules,const char ** error_r)107 module_check_missing_plugin_dependencies(const struct module_dir_load_settings *set,
108 struct module *module,
109 struct module *all_modules,
110 const char **error_r)
111 {
112 const char **deps;
113 struct module *m;
114 string_t *errmsg;
115 size_t len;
116
117 deps = dlsym(module->handle,
118 t_strconcat(module->name, "_dependencies", NULL));
119 if (deps == NULL)
120 return TRUE;
121
122 for (; *deps != NULL; deps++) {
123 len = strlen(*deps);
124 for (m = all_modules; m != NULL; m = m->next) {
125 if (strncmp(m->name, *deps, len) == 0 &&
126 (m->name[len] == '\0' ||
127 strcmp(m->name+len, "_plugin") == 0))
128 break;
129 }
130 if (m == NULL) {
131 errmsg = t_str_new(128);
132 str_printfa(errmsg, "Plugin %s must be loaded also",
133 *deps);
134 if (set->setting_name != NULL) {
135 str_printfa(errmsg,
136 " (you must set: %s=$%s %s)",
137 set->setting_name,
138 set->setting_name, *deps);
139 }
140 *error_r = str_c(errmsg);
141 return FALSE;
142 }
143 }
144 return TRUE;
145 }
146
quiet_dlopen(const char * path,int flags)147 static void *quiet_dlopen(const char *path, int flags)
148 {
149 #ifndef __OpenBSD__
150 return dlopen(path, flags);
151 #else
152 void *handle;
153 int fd;
154
155 /* OpenBSD likes to print all "undefined symbol" errors to stderr.
156 Hide them by sending them to /dev/null. */
157 fd = dup(STDERR_FILENO);
158 if (fd == -1)
159 i_fatal("dup() failed: %m");
160 if (dup2(dev_null_fd, STDERR_FILENO) < 0)
161 i_fatal("dup2() failed: %m");
162 handle = dlopen(path, flags);
163 if (dup2(fd, STDERR_FILENO) < 0)
164 i_fatal("dup2() failed: %m");
165 if (close(fd) < 0)
166 i_error("close() failed: %m");
167 return handle;
168 #endif
169 }
170
versions_equal(const char * str1,const char * str2)171 static bool versions_equal(const char *str1, const char *str2)
172 {
173 while (*str1 == *str2) {
174 if (*str1 == '\0' || *str1 == '(')
175 return TRUE;
176 str1++;
177 str2++;
178 }
179 return FALSE;
180 }
181
182 static int
module_load(const char * path,const char * name,const struct module_dir_load_settings * set,struct module * all_modules,struct module ** module_r,const char ** error_r)183 module_load(const char *path, const char *name,
184 const struct module_dir_load_settings *set,
185 struct module *all_modules,
186 struct module **module_r, const char **error_r)
187 {
188 void *handle;
189 struct module *module;
190 const char *const *module_version;
191 void (*preinit)(void);
192
193 *module_r = NULL;
194 *error_r = NULL;
195
196 if (set->ignore_dlopen_errors) {
197 handle = quiet_dlopen(path, RTLD_GLOBAL | RTLD_NOW);
198 if (handle == NULL) {
199 if (set->debug) {
200 i_debug("Skipping module %s, "
201 "because dlopen() failed: %s "
202 "(this is usually intentional, "
203 "so just ignore this message)",
204 name, dlerror());
205 }
206 return 0;
207 }
208 } else {
209 handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
210 if (handle == NULL) {
211 *error_r = t_strdup_printf("dlopen() failed: %s",
212 dlerror());
213 #ifdef RTLD_LAZY
214 /* try to give a better error message by lazily loading
215 the plugin and checking its dependencies */
216 handle = dlopen(path, RTLD_LAZY);
217 if (handle == NULL)
218 return -1;
219 #else
220 return -1;
221 #endif
222 }
223 }
224
225 module = i_new(struct module, 1);
226 module->path = i_strdup(path);
227 module->name = i_strdup(name);
228 module->handle = handle;
229
230 module_version = set->abi_version == NULL ? NULL :
231 get_symbol(module, t_strconcat(name, "_version", NULL), TRUE);
232 if (module_version != NULL &&
233 !versions_equal(*module_version, set->abi_version)) {
234 *error_r = t_strdup_printf(
235 "Module is for different ABI version %s (we have %s)",
236 *module_version, set->abi_version);
237 module_free(module);
238 return -1;
239 }
240
241 /* get our init func */
242 module->init = (void (*)(struct module *))
243 get_symbol(module, t_strconcat(name, "_init", NULL),
244 !set->require_init_funcs);
245 module->deinit = (void (*)(void))
246 get_symbol(module, t_strconcat(name, "_deinit", NULL),
247 !set->require_init_funcs);
248 preinit = (void (*)(void))
249 get_symbol(module, t_strconcat(name, "_preinit", NULL),
250 TRUE);
251 if (preinit != NULL)
252 preinit();
253
254 if ((module->init == NULL || module->deinit == NULL) &&
255 set->require_init_funcs) {
256 *error_r = t_strdup_printf(
257 "Module doesn't have %s function",
258 module->init == NULL ? "init" : "deinit");
259 } else if (!module_check_wrong_binary_dependency(set, module, error_r)) {
260 /* failed */
261 } else if (!module_check_missing_plugin_dependencies(set, module,
262 all_modules, error_r)) {
263 /* failed */
264 }
265
266 if (*error_r != NULL) {
267 module->deinit = NULL;
268 module_free(module);
269 return -1;
270 }
271
272 if (set->debug)
273 i_debug("Module loaded: %s", path);
274 *module_r = module;
275 return 1;
276 }
277
module_name_cmp(const char * const * n1,const char * const * n2)278 static int module_name_cmp(const char *const *n1, const char *const *n2)
279 {
280 const char *s1 = *n1, *s2 = *n2;
281
282 if (str_begins(s1, "lib"))
283 s1 += 3;
284 if (str_begins(s2, "lib"))
285 s2 += 3;
286
287 return strcmp(s1, s2);
288 }
289
module_want_load(const struct module_dir_load_settings * set,const char ** names,const char * name)290 static bool module_want_load(const struct module_dir_load_settings *set,
291 const char **names, const char *name)
292 {
293 if (set->filter_callback != NULL) {
294 if (!set->filter_callback(name, set->filter_context))
295 return FALSE;
296 }
297 if (names == NULL)
298 return TRUE;
299
300 for (; *names != NULL; names++) {
301 if (strcmp(*names, name) == 0) {
302 *names = "";
303 return TRUE;
304 }
305 }
306 return FALSE;
307 }
308
check_duplicates(ARRAY_TYPE (const_string)* names,const char * name,const char * dir)309 static void check_duplicates(ARRAY_TYPE(const_string) *names,
310 const char *name, const char *dir)
311 {
312 const char *const *names_p, *base_name, *tmp;
313 unsigned int i, count;
314
315 base_name = module_file_get_name(name);
316 names_p = array_get(names, &count);
317 for (i = 0; i < count; i++) T_BEGIN {
318 tmp = module_file_get_name(names_p[i]);
319
320 if (strcmp(tmp, base_name) == 0)
321 i_fatal("Multiple files for module %s: %s/%s, %s/%s",
322 base_name, dir, name, dir, names_p[i]);
323 } T_END;
324 }
325
module_dir_find(struct module * modules,const char * name)326 struct module *module_dir_find(struct module *modules, const char *name)
327 {
328 struct module *module;
329 size_t len = strlen(name);
330
331 for (module = modules; module != NULL; module = module->next) {
332 if (strncmp(module->name, name, len) == 0) {
333 if (module->name[len] == '\0' ||
334 strcmp(module->name + len, "_plugin") == 0)
335 return module;
336 }
337 }
338 return NULL;
339 }
340
module_is_loaded(struct module * modules,const char * name)341 static bool module_is_loaded(struct module *modules, const char *name)
342 {
343 return module_dir_find(modules, name) != NULL;
344 }
345
module_names_fix(const char ** module_names)346 static void module_names_fix(const char **module_names)
347 {
348 unsigned int i, j;
349
350 if (module_names[0] == NULL)
351 return;
352
353 /* allow giving the module names also in non-base form.
354 convert them in here. */
355 for (i = 0; module_names[i] != NULL; i++)
356 module_names[i] = module_file_get_name(module_names[i]);
357
358 /* @UNSAFE: drop duplicates */
359 i_qsort(module_names, i, sizeof(*module_names), i_strcmp_p);
360 for (i = j = 1; module_names[i] != NULL; i++) {
361 if (strcmp(module_names[i-1], module_names[i]) != 0)
362 module_names[j++] = module_names[i];
363 }
364 module_names[j] = NULL;
365 }
366
367 static bool
module_dir_is_all_loaded(struct module * old_modules,const char ** module_names)368 module_dir_is_all_loaded(struct module *old_modules, const char **module_names)
369 {
370 unsigned int i;
371
372 for (i = 0; module_names[i] != NULL; i++) {
373 if (!module_is_loaded(old_modules, module_names[i]))
374 return FALSE;
375 }
376 return TRUE;
377 }
378
379 static int
module_dir_load_real(struct module ** _modules,const char * dir,const char ** module_names,const struct module_dir_load_settings * set,char ** error_r)380 module_dir_load_real(struct module **_modules,
381 const char *dir, const char **module_names,
382 const struct module_dir_load_settings *set,
383 char **error_r)
384 {
385 DIR *dirp;
386 struct dirent *d;
387 const char *name, *p, *error, *const *names_p;
388 struct module *modules, *module, **module_pos, *old_modules = *_modules;
389 unsigned int i, count;
390 ARRAY_TYPE(const_string) names;
391 pool_t pool;
392 int ret;
393
394 *error_r = NULL;
395
396 if (module_names != NULL) {
397 if (module_dir_is_all_loaded(old_modules, module_names))
398 return 0;
399 }
400
401 if (set->debug)
402 i_debug("Loading modules from directory: %s", dir);
403
404 dirp = opendir(dir);
405 if (dirp == NULL) {
406 *error_r = i_strdup_printf("opendir(%s) failed: %m", dir);
407 if (module_names != NULL) {
408 /* we were given a list of modules to load.
409 we can't fail. */
410 return -1;
411 }
412 return errno == ENOENT ? 0 : -1;
413 }
414
415 pool = pool_alloconly_create("module loader", 4096);
416 p_array_init(&names, pool, 32);
417
418 modules = NULL;
419 for (errno = 0; (d = readdir(dirp)) != NULL; errno = 0) {
420 name = d->d_name;
421
422 if (name[0] == '.')
423 continue;
424
425 p = strstr(name, MODULE_SUFFIX);
426 if (p == NULL || strlen(p) != 3)
427 continue;
428
429 T_BEGIN {
430 check_duplicates(&names, name, dir);
431 } T_END;
432
433 name = p_strdup(pool, d->d_name);
434 array_push_back(&names, &name);
435 }
436 if (errno != 0)
437 *error_r = i_strdup_printf("readdir(%s) failed: %m", dir);
438 if (closedir(dirp) < 0 && *error_r == NULL)
439 *error_r = i_strdup_printf("closedir(%s) failed: %m", dir);
440 if (*error_r != NULL) {
441 pool_unref(&pool);
442 return -1;
443 }
444
445 array_sort(&names, module_name_cmp);
446 names_p = array_get(&names, &count);
447
448 modules = old_modules;
449 module_pos = &modules;
450 while (*module_pos != NULL)
451 module_pos = &(*module_pos)->next;
452 for (i = 0; i < count; i++) T_BEGIN {
453 const char *path, *stripped_name, *suffixless_name;
454
455 name = names_p[i];
456 stripped_name = module_file_get_name(name);
457 suffixless_name = module_name_drop_suffix(stripped_name);
458 if (!module_want_load(set, module_names, suffixless_name) ||
459 module_is_loaded(old_modules, suffixless_name))
460 module = NULL;
461 else {
462 path = t_strconcat(dir, "/", name, NULL);
463 ret = module_load(path, stripped_name, set, modules, &module, &error);
464 if (ret >= 0)
465 ;
466 else if (module_names != NULL) {
467 *error_r = i_strdup_printf("Couldn't load required plugin %s: %s",
468 path, error);
469 i = count;
470 } else {
471 i_error("Couldn't load plugin %s: %s", path, error);
472 }
473 }
474
475 if (module != NULL) {
476 *module_pos = module;
477 module_pos = &module->next;
478 }
479 } T_END;
480 pool_unref(&pool);
481
482 if (module_names != NULL && *error_r == NULL && !set->ignore_missing) {
483 /* make sure all modules were found */
484 for (; *module_names != NULL; module_names++) {
485 if (**module_names != '\0') {
486 *error_r = i_strdup_printf("Plugin '%s' not found from directory %s",
487 *module_names, dir);
488 break;
489 }
490 }
491 }
492 *_modules = modules;
493 return *error_r != NULL ? -1 : 0;
494 }
495
module_dir_try_load_missing(struct module ** modules,const char * dir,const char * module_names,const struct module_dir_load_settings * set,const char ** error_r)496 int module_dir_try_load_missing(struct module **modules,
497 const char *dir, const char *module_names,
498 const struct module_dir_load_settings *set,
499 const char **error_r)
500 {
501 char *error = NULL;
502 int ret;
503
504 T_BEGIN {
505 const char **arr = NULL;
506
507 if (module_names != NULL) {
508 arr = t_strsplit_spaces(module_names, ", ");
509 module_names_fix(arr);
510 }
511
512 ret = module_dir_load_real(modules, dir, arr, set, &error);
513 } T_END;
514 *error_r = t_strdup(error);
515 i_free(error);
516 return ret;
517 }
518
519 struct module *
module_dir_load_missing(struct module * old_modules,const char * dir,const char * module_names,const struct module_dir_load_settings * set)520 module_dir_load_missing(struct module *old_modules,
521 const char *dir, const char *module_names,
522 const struct module_dir_load_settings *set)
523 {
524 struct module *new_modules = old_modules;
525 const char *error;
526
527 if (module_dir_try_load_missing(&new_modules, dir, module_names,
528 set, &error) < 0) {
529 if (module_names != NULL)
530 i_fatal("%s", error);
531 else
532 i_error("%s", error);
533 }
534 return new_modules;
535 }
536
module_dir_init(struct module * modules)537 void module_dir_init(struct module *modules)
538 {
539 struct module *module;
540
541 for (module = modules; module != NULL; module = module->next) {
542 if (!module->initialized) {
543 module->initialized = TRUE;
544 if (module->init != NULL) T_BEGIN {
545 module->init(module);
546 } T_END;
547 }
548 }
549 }
550
module_dir_deinit(struct module * modules)551 void module_dir_deinit(struct module *modules)
552 {
553 struct module *module, **rev;
554 unsigned int i, count = 0;
555
556 for (module = modules; module != NULL; module = module->next) {
557 if (module->deinit != NULL && module->initialized)
558 count++;
559 }
560
561 if (count == 0)
562 return;
563
564 /* @UNSAFE: deinitialize in reverse order */
565 T_BEGIN {
566 rev = t_new(struct module *, count);
567 for (i = 0, module = modules; i < count; ) {
568 if (module->deinit != NULL && module->initialized) {
569 rev[count-i-1] = module;
570 i++;
571 }
572 module = module->next;
573 }
574
575 for (i = 0; i < count; i++) {
576 module = rev[i];
577
578 T_BEGIN {
579 module->deinit();
580 } T_END;
581 module->initialized = FALSE;
582 }
583 } T_END;
584 }
585
module_dir_unload(struct module ** modules)586 void module_dir_unload(struct module **modules)
587 {
588 struct module *module, *next;
589
590 /* Call all modules' deinit() first, so that they may still call each
591 others' functions. */
592 module_dir_deinit(*modules);
593
594 for (module = *modules; module != NULL; module = next) {
595 next = module->next;
596 module_free(module);
597 }
598
599 *modules = NULL;
600 }
601
602 #else
603
604 #ifndef MODULE_SUFFIX
605 # define MODULE_SUFFIX ".so" /* just to avoid build failure */
606 #endif
607
608 struct module *
module_dir_load_missing(struct module * old_modules ATTR_UNUSED,const char * dir ATTR_UNUSED,const char * module_names,const struct module_dir_load_settings * set ATTR_UNUSED)609 module_dir_load_missing(struct module *old_modules ATTR_UNUSED,
610 const char *dir ATTR_UNUSED,
611 const char *module_names,
612 const struct module_dir_load_settings *set ATTR_UNUSED)
613 {
614 #define NO_SUPPORT_ERRSTR "Dynamically loadable module support not built in"
615 if (module_names == NULL)
616 i_error(NO_SUPPORT_ERRSTR);
617 else {
618 i_fatal(NO_SUPPORT_ERRSTR", can't load plugins: %s",
619 module_names);
620 }
621 return NULL;
622 }
623
module_dir_init(struct module * modules ATTR_UNUSED)624 void module_dir_init(struct module *modules ATTR_UNUSED)
625 {
626 }
627
module_dir_deinit(struct module * modules ATTR_UNUSED)628 void module_dir_deinit(struct module *modules ATTR_UNUSED)
629 {
630 }
631
module_dir_unload(struct module ** modules ATTR_UNUSED)632 void module_dir_unload(struct module **modules ATTR_UNUSED)
633 {
634 }
635
module_dir_find(struct module * modules ATTR_UNUSED,const char * name ATTR_UNUSED)636 struct module *module_dir_find(struct module *modules ATTR_UNUSED,
637 const char *name ATTR_UNUSED)
638 {
639 return NULL;
640 }
641
module_get_symbol(struct module * module ATTR_UNUSED,const char * symbol ATTR_UNUSED)642 void *module_get_symbol(struct module *module ATTR_UNUSED,
643 const char *symbol ATTR_UNUSED)
644 {
645 return NULL;
646 }
647
module_get_symbol_quiet(struct module * module ATTR_UNUSED,const char * symbol ATTR_UNUSED)648 void *module_get_symbol_quiet(struct module *module ATTR_UNUSED,
649 const char *symbol ATTR_UNUSED)
650 {
651 return NULL;
652 }
653
654 #endif
655
module_dir_load(const char * dir,const char * module_names,const struct module_dir_load_settings * set)656 struct module *module_dir_load(const char *dir, const char *module_names,
657 const struct module_dir_load_settings *set)
658 {
659 return module_dir_load_missing(NULL, dir, module_names, set);
660 }
661
module_file_get_name(const char * fname)662 const char *module_file_get_name(const char *fname)
663 {
664 const char *p;
665
666 /* [lib][nn_]name(.so) */
667 if (str_begins(fname, "lib"))
668 fname += 3;
669
670 for (p = fname; *p != '\0'; p++) {
671 if (*p < '0' || *p > '9')
672 break;
673 }
674 if (*p == '_')
675 fname = p + 1;
676
677 p = strstr(fname, MODULE_SUFFIX);
678 if (p == NULL)
679 return fname;
680
681 return t_strdup_until(fname, p);
682 }
683
module_name_drop_suffix(const char * name)684 static const char *module_name_drop_suffix(const char *name)
685 {
686 size_t len;
687
688 len = strlen(name);
689 if (len > 7 && strcmp(name + len - 7, "_plugin") == 0)
690 name = t_strndup(name, len - 7);
691 return name;
692 }
693
module_get_plugin_name(struct module * module)694 const char *module_get_plugin_name(struct module *module)
695 {
696 return module_name_drop_suffix(module->name);
697 }
698