Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/util/support/plugins.c
34889 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* util/support/plugins.c - Plugin module support functions */
3
/*
4
* Copyright 2006, 2008 by the Massachusetts Institute of Technology.
5
* All Rights Reserved.
6
*
7
* Export of this software from the United States of America may
8
* require a specific license from the United States Government.
9
* It is the responsibility of any person or organization contemplating
10
* export to obtain such a license before exporting.
11
*
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of M.I.T. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. Furthermore if you modify this software you must label
20
* your software as modified software and not distribute it in such a
21
* fashion that it might be confused with the original M.I.T. software.
22
* M.I.T. makes no representations about the suitability of
23
* this software for any purpose. It is provided "as is" without express
24
* or implied warranty.
25
*/
26
27
#include "k5-platform.h"
28
#include "k5-plugin.h"
29
#if USE_DLOPEN
30
#include <dlfcn.h>
31
#endif
32
33
#if USE_DLOPEN
34
#ifdef RTLD_GROUP
35
#define GROUP RTLD_GROUP
36
#else
37
#define GROUP 0
38
#endif
39
#ifdef RTLD_NODELETE
40
#define NODELETE RTLD_NODELETE
41
#else
42
#define NODELETE 0
43
#endif
44
#define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | GROUP | NODELETE)
45
#endif
46
47
/*
48
* glibc bug 11941, fixed in release 2.25, can cause an assertion failure in
49
* dlclose() on process exit. Our workaround is to leak dlopen() handles
50
* (which doesn't typically manifest in leak detection tools because the
51
* handles are still reachable via a global table in libdl). Because we
52
* dlopen() with RTLD_NODELETE, we weren't going to unload the plugin objects
53
* anyway.
54
*/
55
#ifdef __GLIBC_PREREQ
56
#if ! __GLIBC_PREREQ(2, 25)
57
#define dlclose(x)
58
#endif
59
#endif
60
61
#include <stdarg.h>
62
static void Tprintf (const char *fmt, ...)
63
{
64
#ifdef DEBUG
65
va_list va;
66
va_start (va, fmt);
67
vfprintf (stderr, fmt, va);
68
va_end (va);
69
#endif
70
}
71
72
struct plugin_file_handle {
73
#if defined(USE_DLOPEN)
74
void *dlhandle;
75
#elif defined(_WIN32)
76
HMODULE module;
77
#else
78
char dummy;
79
#endif
80
};
81
82
#if defined(USE_DLOPEN)
83
84
static long
85
open_plugin_dlfcn(struct plugin_file_handle *h, const char *filename,
86
struct errinfo *ep)
87
{
88
const char *e;
89
90
h->dlhandle = dlopen(filename, PLUGIN_DLOPEN_FLAGS);
91
if (h->dlhandle == NULL) {
92
e = dlerror();
93
if (e == NULL)
94
e = _("unknown failure");
95
Tprintf("dlopen(%s): %s\n", filename, e);
96
k5_set_error(ep, ENOENT, _("unable to load plugin [%s]: %s"),
97
filename, e);
98
return ENOENT;
99
}
100
return 0;
101
}
102
#define open_plugin open_plugin_dlfcn
103
104
static long
105
get_sym_dlfcn(struct plugin_file_handle *h, const char *csymname,
106
void **sym_out, struct errinfo *ep)
107
{
108
const char *e;
109
110
if (h->dlhandle == NULL)
111
return ENOENT;
112
*sym_out = dlsym(h->dlhandle, csymname);
113
if (*sym_out == NULL) {
114
e = dlerror();
115
if (e == NULL)
116
e = _("unknown failure");
117
Tprintf("dlsym(%s): %s\n", csymname, e);
118
k5_set_error(ep, ENOENT, "%s", e);
119
return ENOENT;
120
}
121
return 0;
122
}
123
#define get_sym get_sym_dlfcn
124
125
static void
126
close_plugin_dlfcn(struct plugin_file_handle *h)
127
{
128
if (h->dlhandle != NULL)
129
dlclose(h->dlhandle);
130
}
131
#define close_plugin close_plugin_dlfcn
132
133
#elif defined(_WIN32)
134
135
static long
136
open_plugin_win32(struct plugin_file_handle *h, const char *filename,
137
struct errinfo *ep)
138
{
139
h->module = LoadLibrary(filename);
140
if (h == NULL) {
141
Tprintf("Unable to load dll: %s\n", filename);
142
k5_set_error(ep, ENOENT, _("unable to load DLL [%s]"), filename);
143
return ENOENT;
144
}
145
return 0;
146
}
147
#define open_plugin open_plugin_win32
148
149
static long
150
get_sym_win32(struct plugin_file_handle *h, const char *csymname,
151
void **sym_out, struct errinfo *ep)
152
{
153
LPVOID lpMsgBuf;
154
DWORD dw;
155
156
if (h->module == NULL)
157
return ENOENT;
158
*sym_out = GetProcAddress(h->module, csymname);
159
if (*sym_out == NULL) {
160
Tprintf("GetProcAddress(%s): %i\n", csymname, GetLastError());
161
dw = GetLastError();
162
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
163
FORMAT_MESSAGE_FROM_SYSTEM,
164
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
165
(LPTSTR)&lpMsgBuf, 0, NULL)) {
166
k5_set_error(ep, ENOENT, _("unable to get DLL Symbol: %s"),
167
(char *)lpMsgBuf);
168
LocalFree(lpMsgBuf);
169
}
170
return ENOENT;
171
}
172
return 0;
173
}
174
#define get_sym get_sym_win32
175
176
static void
177
close_plugin_win32(struct plugin_file_handle *h)
178
{
179
if (h->module != NULL)
180
FreeLibrary(h->module);
181
}
182
#define close_plugin close_plugin_win32
183
184
#else
185
186
static long
187
open_plugin_dummy(struct plugin_file_handle *h, const char *filename,
188
struct errinfo *ep)
189
{
190
k5_set_error(ep, ENOENT, _("plugin loading unavailable"));
191
return ENOENT;
192
}
193
#define open_plugin open_plugin_dummy
194
195
static long
196
get_sym_dummy(struct plugin_file_handle *h, const char *csymname,
197
void **sym_out, struct errinfo *ep)
198
{
199
return ENOENT;
200
}
201
#define get_sym get_sym_dummy
202
203
static void
204
close_plugin_dummy(struct plugin_file_handle *h)
205
{
206
}
207
#define close_plugin close_plugin_dummy
208
209
#endif
210
211
long KRB5_CALLCONV
212
krb5int_open_plugin(const char *filename,
213
struct plugin_file_handle **handle_out, struct errinfo *ep)
214
{
215
long ret;
216
struct plugin_file_handle *h;
217
218
*handle_out = NULL;
219
220
h = calloc(1, sizeof(*h));
221
if (h == NULL)
222
return ENOMEM;
223
224
ret = open_plugin(h, filename, ep);
225
if (ret) {
226
free(h);
227
return ret;
228
}
229
230
*handle_out = h;
231
return 0;
232
}
233
234
long KRB5_CALLCONV
235
krb5int_get_plugin_data(struct plugin_file_handle *h, const char *csymname,
236
void **sym_out, struct errinfo *ep)
237
{
238
return get_sym(h, csymname, sym_out, ep);
239
}
240
241
long KRB5_CALLCONV
242
krb5int_get_plugin_func(struct plugin_file_handle *h, const char *csymname,
243
void (**sym_out)(void), struct errinfo *ep)
244
{
245
void *dptr = NULL;
246
long ret = get_sym(h, csymname, &dptr, ep);
247
248
if (!ret)
249
*sym_out = (void (*)(void))dptr;
250
return ret;
251
}
252
253
void KRB5_CALLCONV
254
krb5int_close_plugin (struct plugin_file_handle *h)
255
{
256
close_plugin(h);
257
free(h);
258
}
259
260
static long
261
krb5int_plugin_file_handle_array_init (struct plugin_file_handle ***harray)
262
{
263
long err = 0;
264
265
*harray = calloc (1, sizeof (**harray)); /* calloc initializes to NULL */
266
if (*harray == NULL) { err = ENOMEM; }
267
268
return err;
269
}
270
271
static long
272
krb5int_plugin_file_handle_array_add (struct plugin_file_handle ***harray, size_t *count,
273
struct plugin_file_handle *p)
274
{
275
long err = 0;
276
struct plugin_file_handle **newharray = NULL;
277
size_t newcount = *count + 1;
278
279
newharray = realloc (*harray, ((newcount + 1) * sizeof (**harray))); /* +1 for NULL */
280
if (newharray == NULL) {
281
err = ENOMEM;
282
} else {
283
newharray[newcount - 1] = p;
284
newharray[newcount] = NULL;
285
*count = newcount;
286
*harray = newharray;
287
}
288
289
return err;
290
}
291
292
static void
293
krb5int_plugin_file_handle_array_free (struct plugin_file_handle **harray)
294
{
295
size_t i;
296
297
if (harray != NULL) {
298
for (i = 0; harray[i] != NULL; i++) {
299
krb5int_close_plugin (harray[i]);
300
}
301
free (harray);
302
}
303
}
304
305
#if TARGET_OS_MAC
306
#define FILEEXTS { "", ".bundle", ".dylib", ".so", NULL }
307
#elif defined(_WIN32)
308
#define FILEEXTS { "", ".dll", NULL }
309
#else
310
#define FILEEXTS { "", ".so", NULL }
311
#endif
312
313
314
static void
315
krb5int_free_plugin_filenames (char **filenames)
316
{
317
size_t i;
318
319
if (filenames != NULL) {
320
for (i = 0; filenames[i] != NULL; i++) {
321
free (filenames[i]);
322
}
323
free (filenames);
324
}
325
}
326
327
328
static long
329
krb5int_get_plugin_filenames (const char * const *filebases, char ***filenames)
330
{
331
long err = 0;
332
static const char *const fileexts[] = FILEEXTS;
333
char **tempnames = NULL;
334
size_t bases_count = 0;
335
size_t exts_count = 0;
336
size_t i;
337
338
if (!filebases) { err = EINVAL; }
339
if (!filenames) { err = EINVAL; }
340
341
if (!err) {
342
for (i = 0; filebases[i]; i++) { bases_count++; }
343
for (i = 0; fileexts[i]; i++) { exts_count++; }
344
tempnames = calloc ((bases_count * exts_count)+1, sizeof (char *));
345
if (!tempnames) { err = ENOMEM; }
346
}
347
348
if (!err) {
349
size_t j;
350
for (i = 0; !err && filebases[i]; i++) {
351
for (j = 0; !err && fileexts[j]; j++) {
352
if (asprintf(&tempnames[(i*exts_count)+j], "%s%s",
353
filebases[i], fileexts[j]) < 0) {
354
tempnames[(i*exts_count)+j] = NULL;
355
err = ENOMEM;
356
}
357
}
358
}
359
tempnames[bases_count * exts_count] = NULL; /* NUL-terminate */
360
}
361
362
if (!err) {
363
*filenames = tempnames;
364
tempnames = NULL;
365
}
366
367
krb5int_free_plugin_filenames(tempnames);
368
369
return err;
370
}
371
372
373
/* Takes a NULL-terminated list of directories. If filebases is NULL, filebases is ignored
374
* all plugins in the directories are loaded. If filebases is a NULL-terminated array of names,
375
* only plugins in the directories with those name (plus any platform extension) are loaded. */
376
377
long KRB5_CALLCONV
378
krb5int_open_plugin_dirs (const char * const *dirnames,
379
const char * const *filebases,
380
struct plugin_dir_handle *dirhandle,
381
struct errinfo *ep)
382
{
383
long err = 0;
384
struct plugin_file_handle **h = NULL;
385
size_t count = 0;
386
char **filenames = NULL;
387
size_t i;
388
389
if (!err) {
390
err = krb5int_plugin_file_handle_array_init (&h);
391
}
392
393
if (!err && (filebases != NULL)) {
394
err = krb5int_get_plugin_filenames (filebases, &filenames);
395
}
396
397
for (i = 0; !err && dirnames[i] != NULL; i++) {
398
if (filenames != NULL) {
399
/* load plugins with names from filenames from each directory */
400
size_t j;
401
402
for (j = 0; !err && filenames[j] != NULL; j++) {
403
struct plugin_file_handle *handle = NULL;
404
char *filepath = NULL;
405
406
if (!err)
407
err = k5_path_join(dirnames[i], filenames[j], &filepath);
408
409
if (!err && krb5int_open_plugin(filepath, &handle, ep) == 0) {
410
err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
411
if (!err)
412
handle = NULL; /* h takes ownership */
413
}
414
415
free(filepath);
416
if (handle != NULL) { krb5int_close_plugin (handle); }
417
}
418
} else {
419
char **fnames = NULL;
420
size_t j;
421
422
err = k5_dir_filenames(dirnames[i], &fnames);
423
for (j = 0; !err && fnames[j] != NULL; j++) {
424
char *filepath = NULL;
425
struct plugin_file_handle *handle = NULL;
426
427
if (strcmp(fnames[j], ".") == 0 ||
428
strcmp(fnames[j], "..") == 0)
429
continue;
430
431
err = k5_path_join(dirnames[i], fnames[j], &filepath);
432
433
if (!err && krb5int_open_plugin(filepath, &handle, ep) == 0) {
434
err = krb5int_plugin_file_handle_array_add(&h, &count,
435
handle);
436
if (!err)
437
handle = NULL; /* h takes ownership */
438
}
439
440
free(filepath);
441
if (handle != NULL)
442
krb5int_close_plugin(handle);
443
}
444
445
k5_free_filenames(fnames);
446
}
447
}
448
449
if (err == ENOENT) {
450
err = 0; /* ran out of plugins -- do nothing */
451
}
452
453
if (!err) {
454
dirhandle->files = h;
455
h = NULL; /* dirhandle->files takes ownership */
456
}
457
458
if (filenames != NULL) { krb5int_free_plugin_filenames (filenames); }
459
if (h != NULL) { krb5int_plugin_file_handle_array_free (h); }
460
461
return err;
462
}
463
464
void KRB5_CALLCONV
465
krb5int_close_plugin_dirs (struct plugin_dir_handle *dirhandle)
466
{
467
size_t i;
468
469
if (dirhandle->files != NULL) {
470
for (i = 0; dirhandle->files[i] != NULL; i++) {
471
krb5int_close_plugin (dirhandle->files[i]);
472
}
473
free (dirhandle->files);
474
dirhandle->files = NULL;
475
}
476
}
477
478
void KRB5_CALLCONV
479
krb5int_free_plugin_dir_data (void **ptrs)
480
{
481
/* Nothing special to be done per pointer. */
482
free(ptrs);
483
}
484
485
long KRB5_CALLCONV
486
krb5int_get_plugin_dir_data (struct plugin_dir_handle *dirhandle,
487
const char *symname,
488
void ***ptrs,
489
struct errinfo *ep)
490
{
491
long err = 0;
492
void **p = NULL;
493
size_t count = 0;
494
495
/* XXX Do we need to add a leading "_" to the symbol name on any
496
modern platforms? */
497
498
Tprintf("get_plugin_data_sym(%s)\n", symname);
499
500
if (!err) {
501
p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
502
if (p == NULL) { err = ENOMEM; }
503
}
504
505
if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
506
size_t i = 0;
507
508
for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
509
void *sym = NULL;
510
511
if (krb5int_get_plugin_data (dirhandle->files[i], symname, &sym, ep) == 0) {
512
void **newp = NULL;
513
514
count++;
515
newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
516
if (newp == NULL) {
517
err = ENOMEM;
518
} else {
519
p = newp;
520
p[count - 1] = sym;
521
p[count] = NULL;
522
}
523
}
524
}
525
}
526
527
if (!err) {
528
*ptrs = p;
529
p = NULL; /* ptrs takes ownership */
530
}
531
532
free(p);
533
534
return err;
535
}
536
537
void KRB5_CALLCONV
538
krb5int_free_plugin_dir_func (void (**ptrs)(void))
539
{
540
/* Nothing special to be done per pointer. */
541
free(ptrs);
542
}
543
544
long KRB5_CALLCONV
545
krb5int_get_plugin_dir_func (struct plugin_dir_handle *dirhandle,
546
const char *symname,
547
void (***ptrs)(void),
548
struct errinfo *ep)
549
{
550
long err = 0;
551
void (**p)(void) = NULL;
552
size_t count = 0;
553
554
/* XXX Do we need to add a leading "_" to the symbol name on any
555
modern platforms? */
556
557
Tprintf("get_plugin_data_sym(%s)\n", symname);
558
559
if (!err) {
560
p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
561
if (p == NULL) { err = ENOMEM; }
562
}
563
564
if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
565
size_t i = 0;
566
567
for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
568
void (*sym)(void) = NULL;
569
570
if (krb5int_get_plugin_func (dirhandle->files[i], symname, &sym, ep) == 0) {
571
void (**newp)(void) = NULL;
572
573
count++;
574
newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
575
if (newp == NULL) {
576
err = ENOMEM;
577
} else {
578
p = newp;
579
p[count - 1] = sym;
580
p[count] = NULL;
581
}
582
}
583
}
584
}
585
586
if (!err) {
587
*ptrs = p;
588
p = NULL; /* ptrs takes ownership */
589
}
590
591
free(p);
592
593
return err;
594
}
595
596