47368be9d49be24250272b91147c64a343021949
[platform/upstream/krb5.git] / src / util / support / plugins.c
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 #include <sys/types.h>
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h>
35 #endif
36 #ifdef HAVE_SYS_PARAM_H
37 #include <sys/param.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #if USE_DLOPEN
44 #ifdef RTLD_GROUP
45 #define GROUP RTLD_GROUP
46 #else
47 #define GROUP 0
48 #endif
49 #ifdef RTLD_NODELETE
50 #define NODELETE RTLD_NODELETE
51 #else
52 #define NODELETE 0
53 #endif
54 #define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | GROUP | NODELETE)
55 #endif
56
57 #if USE_DLOPEN && USE_CFBUNDLE
58 #include <CoreFoundation/CoreFoundation.h>
59
60 /* Currently CoreFoundation only exists on the Mac so we just use
61  * pthreads directly to avoid creating empty function calls on other
62  * platforms.  If a thread initializer ever gets created in the common
63  * plugin code, move this there */
64 static pthread_mutex_t krb5int_bundle_mutex = PTHREAD_MUTEX_INITIALIZER;
65 #endif
66
67 #include <stdarg.h>
68 static void Tprintf (const char *fmt, ...)
69 {
70 #ifdef DEBUG
71     va_list va;
72     va_start (va, fmt);
73     vfprintf (stderr, fmt, va);
74     va_end (va);
75 #endif
76 }
77
78 struct plugin_file_handle {
79 #if USE_DLOPEN
80     void *dlhandle;
81 #endif
82 #ifdef _WIN32
83     HMODULE hinstPlugin;
84 #endif
85 #if !defined (USE_DLOPEN) && !defined (_WIN32)
86     char dummy;
87 #endif
88 };
89
90 #ifdef _WIN32
91 struct dirent {
92     long d_ino;                 /* inode (always 1 in WIN32) */
93     off_t d_off;                /* offset to this dirent */
94     unsigned short d_reclen;    /* length of d_name */
95     char d_name[_MAX_FNAME+1];  /* filename (null terminated) */
96 };
97
98 typedef struct {
99     intptr_t handle;            /* _findfirst/_findnext handle */
100     short offset;               /* offset into directory */
101     short finished;             /* 1 if there are not more files */
102     struct _finddata_t fileinfo;/* from _findfirst/_findnext */
103     char *dir;                  /* the dir we are reading */
104     struct dirent dent;         /* the dirent to return */
105 } DIR;
106
107 DIR * opendir(const char *dir)
108 {
109     DIR *dp;
110     char *filespec;
111     intptr_t handle;
112     int index;
113
114     filespec = malloc(strlen(dir) + 2 + 1);
115     strcpy(filespec, dir);
116     index = strlen(filespec) - 1;
117     if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))
118         filespec[index] = '\0';
119     strcat(filespec, "/*");
120
121     dp = (DIR *)malloc(sizeof(DIR));
122     dp->offset = 0;
123     dp->finished = 0;
124     dp->dir = strdup(dir);
125
126     if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0) {
127         if (errno == ENOENT)
128             dp->finished = 1;
129         else {
130             free(filespec);
131             free(dp->dir);
132             free(dp);
133             return NULL;
134         }
135     }
136
137     dp->handle = handle;
138     free(filespec);
139
140     return dp;
141 }
142
143 struct dirent * readdir(DIR *dp)
144 {
145     if (!dp || dp->finished) return NULL;
146
147     if (dp->offset != 0) {
148         if (_findnext(dp->handle, &(dp->fileinfo)) < 0) {
149             dp->finished = 1;
150             return NULL;
151         }
152     }
153     dp->offset++;
154
155     strncpy(dp->dent.d_name, dp->fileinfo.name, _MAX_FNAME);
156     dp->dent.d_ino = 1;
157     dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
158     dp->dent.d_off = dp->offset;
159
160     return &(dp->dent);
161 }
162
163 int closedir(DIR *dp)
164 {
165     if (!dp) return 0;
166     _findclose(dp->handle);
167     free(dp->dir);
168     free(dp);
169
170     return 0;
171 }
172 #endif
173
174 long KRB5_CALLCONV
175 krb5int_open_plugin (const char *filepath, struct plugin_file_handle **h, struct errinfo *ep)
176 {
177     long err = 0;
178     struct stat statbuf;
179     struct plugin_file_handle *htmp = NULL;
180     int got_plugin = 0;
181
182     if (!err) {
183         if (stat (filepath, &statbuf) < 0) {
184             err = errno;
185             Tprintf ("stat(%s): %s\n", filepath, strerror (err));
186             k5_set_error(ep, err, _("unable to find plugin [%s]: %s"),
187                          filepath, strerror(err));
188         }
189     }
190
191     if (!err) {
192         htmp = calloc (1, sizeof (*htmp)); /* calloc initializes ptrs to NULL */
193         if (htmp == NULL) { err = ENOMEM; }
194     }
195
196 #if USE_DLOPEN
197     if (!err && ((statbuf.st_mode & S_IFMT) == S_IFREG
198 #if USE_CFBUNDLE
199                  || (statbuf.st_mode & S_IFMT) == S_IFDIR
200 #endif /* USE_CFBUNDLE */
201         )) {
202         void *handle = NULL;
203
204 #if USE_CFBUNDLE
205         char executablepath[MAXPATHLEN];
206
207         if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
208             int lock_err = 0;
209             CFStringRef pluginString = NULL;
210             CFURLRef pluginURL = NULL;
211             CFBundleRef pluginBundle = NULL;
212             CFURLRef executableURL = NULL;
213
214             /* Lock around CoreFoundation calls since objects are refcounted
215              * and the refcounts are not thread-safe.  Using pthreads directly
216              * because this code is Mac-specific */
217             lock_err = pthread_mutex_lock(&krb5int_bundle_mutex);
218             if (lock_err) { err = lock_err; }
219
220             if (!err) {
221                 pluginString = CFStringCreateWithCString (kCFAllocatorDefault,
222                                                           filepath,
223                                                           kCFStringEncodingASCII);
224                 if (pluginString == NULL) { err = ENOMEM; }
225             }
226
227             if (!err) {
228                 pluginURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault,
229                                                            pluginString,
230                                                            kCFURLPOSIXPathStyle,
231                                                            true);
232                 if (pluginURL == NULL) { err = ENOMEM; }
233             }
234
235             if (!err) {
236                 pluginBundle = CFBundleCreate (kCFAllocatorDefault, pluginURL);
237                 if (pluginBundle == NULL) { err = ENOENT; } /* XXX need better error */
238             }
239
240             if (!err) {
241                 executableURL = CFBundleCopyExecutableURL (pluginBundle);
242                 if (executableURL == NULL) { err = ENOMEM; }
243             }
244
245             if (!err) {
246                 if (!CFURLGetFileSystemRepresentation (executableURL,
247                                                        true, /* absolute */
248                                                        (UInt8 *)executablepath,
249                                                        sizeof (executablepath))) {
250                     err = ENOMEM;
251                 }
252             }
253
254             if (!err) {
255                 /* override the path the caller passed in */
256                 filepath = executablepath;
257             }
258
259             if (executableURL    != NULL) { CFRelease (executableURL); }
260             if (pluginBundle     != NULL) { CFRelease (pluginBundle); }
261             if (pluginURL        != NULL) { CFRelease (pluginURL); }
262             if (pluginString     != NULL) { CFRelease (pluginString); }
263
264             /* unlock after CFRelease calls since they modify refcounts */
265             if (!lock_err) { pthread_mutex_unlock (&krb5int_bundle_mutex); }
266         }
267 #endif /* USE_CFBUNDLE */
268
269         if (!err) {
270             handle = dlopen(filepath, PLUGIN_DLOPEN_FLAGS);
271             if (handle == NULL) {
272                 const char *e = dlerror();
273                 if (e == NULL)
274                     e = _("unknown failure");
275                 Tprintf ("dlopen(%s): %s\n", filepath, e);
276                 err = ENOENT; /* XXX */
277                 k5_set_error(ep, err, _("unable to load plugin [%s]: %s"),
278                              filepath, e);
279             }
280         }
281
282         if (!err) {
283             got_plugin = 1;
284             htmp->dlhandle = handle;
285             handle = NULL;
286         }
287
288         if (handle != NULL) { dlclose (handle); }
289     }
290 #endif /* USE_DLOPEN */
291
292 #ifdef _WIN32
293     if (!err && (statbuf.st_mode & S_IFMT) == S_IFREG) {
294         HMODULE handle = NULL;
295
296         handle = LoadLibrary(filepath);
297         if (handle == NULL) {
298             Tprintf ("Unable to load dll: %s\n", filepath);
299             err = ENOENT; /* XXX */
300             k5_set_error(ep, err, _("unable to load DLL [%s]"), filepath);
301         }
302
303         if (!err) {
304             got_plugin = 1;
305             htmp->hinstPlugin = handle;
306             handle = NULL;
307         }
308
309         if (handle != NULL)
310             FreeLibrary(handle);
311     }
312 #endif
313
314     if (!err && !got_plugin) {
315         err = ENOENT;  /* no plugin or no way to load plugins */
316         k5_set_error(ep, err, _("plugin unavailable: %s"), strerror(err));
317     }
318
319     if (!err) {
320         *h = htmp;
321         htmp = NULL;  /* h takes ownership */
322     }
323
324     free(htmp);
325
326     return err;
327 }
328
329 static long
330 krb5int_get_plugin_sym (struct plugin_file_handle *h,
331                         const char *csymname, int isfunc, void **ptr,
332                         struct errinfo *ep)
333 {
334     long err = 0;
335     void *sym = NULL;
336
337 #if USE_DLOPEN
338     if (!err && !sym && (h->dlhandle != NULL)) {
339         /* XXX Do we need to add a leading "_" to the symbol name on any
340            modern platforms?  */
341         sym = dlsym (h->dlhandle, csymname);
342         if (sym == NULL) {
343             const char *e = dlerror (); /* XXX copy and save away */
344             if (e == NULL)
345                 e = "unknown failure";
346             Tprintf ("dlsym(%s): %s\n", csymname, e);
347             err = ENOENT; /* XXX */
348             k5_set_error(ep, err, "%s", e);
349         }
350     }
351 #endif
352
353 #ifdef _WIN32
354     LPVOID lpMsgBuf;
355     DWORD dw;
356
357     if (!err && !sym && (h->hinstPlugin != NULL)) {
358         sym = GetProcAddress(h->hinstPlugin, csymname);
359         if (sym == NULL) {
360             const char *e = "unable to get dll symbol"; /* XXX copy and save away */
361             Tprintf ("GetProcAddress(%s): %i\n", csymname, GetLastError());
362             err = ENOENT; /* XXX */
363             k5_set_error(ep, err, "%s", e);
364
365             dw = GetLastError();
366             if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
367                               FORMAT_MESSAGE_FROM_SYSTEM,
368                               NULL,
369                               dw,
370                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
371                               (LPTSTR) &lpMsgBuf,
372                               0, NULL )) {
373
374                 fprintf (stderr, "unable to get dll symbol, %s\n", (LPCTSTR)lpMsgBuf);
375                 LocalFree(lpMsgBuf);
376             }
377         }
378     }
379 #endif
380
381     if (!err && (sym == NULL)) {
382         err = ENOENT;  /* unimplemented */
383     }
384
385     if (!err) {
386         *ptr = sym;
387     }
388
389     return err;
390 }
391
392 long KRB5_CALLCONV
393 krb5int_get_plugin_data (struct plugin_file_handle *h, const char *csymname,
394                          void **ptr, struct errinfo *ep)
395 {
396     return krb5int_get_plugin_sym (h, csymname, 0, ptr, ep);
397 }
398
399 long KRB5_CALLCONV
400 krb5int_get_plugin_func (struct plugin_file_handle *h, const char *csymname,
401                          void (**ptr)(), struct errinfo *ep)
402 {
403     void *dptr = NULL;
404     long err = krb5int_get_plugin_sym (h, csymname, 1, &dptr, ep);
405     if (!err) {
406         /* Cast function pointers to avoid code duplication */
407         *ptr = (void (*)()) dptr;
408     }
409     return err;
410 }
411
412 void KRB5_CALLCONV
413 krb5int_close_plugin (struct plugin_file_handle *h)
414 {
415 #if USE_DLOPEN
416     if (h->dlhandle != NULL) { dlclose(h->dlhandle); }
417 #endif
418 #ifdef _WIN32
419     if (h->hinstPlugin != NULL) { FreeLibrary(h->hinstPlugin); }
420 #endif
421     free (h);
422 }
423
424 /* autoconf docs suggest using this preference order */
425 #if HAVE_DIRENT_H || USE_DIRENT_H
426 #include <dirent.h>
427 #define NAMELEN(D) strlen((D)->d_name)
428 #else
429 #ifndef _WIN32
430 #define dirent direct
431 #define NAMELEN(D) ((D)->d->namlen)
432 #else
433 #define NAMELEN(D) strlen((D)->d_name)
434 #endif
435 #if HAVE_SYS_NDIR_H
436 # include <sys/ndir.h>
437 #elif HAVE_SYS_DIR_H
438 # include <sys/dir.h>
439 #elif HAVE_NDIR_H
440 # include <ndir.h>
441 #endif
442 #endif
443
444 static long
445 krb5int_plugin_file_handle_array_init (struct plugin_file_handle ***harray)
446 {
447     long err = 0;
448
449     *harray = calloc (1, sizeof (**harray)); /* calloc initializes to NULL */
450     if (*harray == NULL) { err = ENOMEM; }
451
452     return err;
453 }
454
455 static long
456 krb5int_plugin_file_handle_array_add (struct plugin_file_handle ***harray, size_t *count,
457                                       struct plugin_file_handle *p)
458 {
459     long err = 0;
460     struct plugin_file_handle **newharray = NULL;
461     size_t newcount = *count + 1;
462
463     newharray = realloc (*harray, ((newcount + 1) * sizeof (**harray))); /* +1 for NULL */
464     if (newharray == NULL) {
465         err = ENOMEM;
466     } else {
467         newharray[newcount - 1] = p;
468         newharray[newcount] = NULL;
469         *count = newcount;
470         *harray = newharray;
471     }
472
473     return err;
474 }
475
476 static void
477 krb5int_plugin_file_handle_array_free (struct plugin_file_handle **harray)
478 {
479     if (harray != NULL) {
480         int i;
481         for (i = 0; harray[i] != NULL; i++) {
482             krb5int_close_plugin (harray[i]);
483         }
484         free (harray);
485     }
486 }
487
488 #if TARGET_OS_MAC
489 #define FILEEXTS { "", ".bundle", ".dylib", ".so", NULL }
490 #elif defined(_WIN32)
491 #define FILEEXTS  { "", ".dll", NULL }
492 #else
493 #define FILEEXTS  { "", ".so", NULL }
494 #endif
495
496
497 static void
498 krb5int_free_plugin_filenames (char **filenames)
499 {
500     if (filenames != NULL) {
501         int i;
502         for (i = 0; filenames[i] != NULL; i++) {
503             free (filenames[i]);
504         }
505         free (filenames);
506     }
507 }
508
509
510 static long
511 krb5int_get_plugin_filenames (const char * const *filebases, char ***filenames)
512 {
513     long err = 0;
514     static const char *const fileexts[] = FILEEXTS;
515     char **tempnames = NULL;
516     size_t bases_count = 0;
517     size_t exts_count = 0;
518     size_t i;
519
520     if (!filebases) { err = EINVAL; }
521     if (!filenames) { err = EINVAL; }
522
523     if (!err) {
524         for (i = 0; filebases[i]; i++) { bases_count++; }
525         for (i = 0; fileexts[i]; i++) { exts_count++; }
526         tempnames = calloc ((bases_count * exts_count)+1, sizeof (char *));
527         if (!tempnames) { err = ENOMEM; }
528     }
529
530     if (!err) {
531         size_t j;
532         for (i = 0; !err && filebases[i]; i++) {
533             for (j = 0; !err && fileexts[j]; j++) {
534                 if (asprintf(&tempnames[(i*exts_count)+j], "%s%s",
535                              filebases[i], fileexts[j]) < 0) {
536                     tempnames[(i*exts_count)+j] = NULL;
537                     err = ENOMEM;
538                 }
539             }
540         }
541         tempnames[bases_count * exts_count] = NULL; /* NUL-terminate */
542     }
543
544     if (!err) {
545         *filenames = tempnames;
546         tempnames = NULL;
547     }
548
549     krb5int_free_plugin_filenames(tempnames);
550
551     return err;
552 }
553
554
555 /* Takes a NULL-terminated list of directories.  If filebases is NULL, filebases is ignored
556  * all plugins in the directories are loaded.  If filebases is a NULL-terminated array of names,
557  * only plugins in the directories with those name (plus any platform extension) are loaded. */
558
559 long KRB5_CALLCONV
560 krb5int_open_plugin_dirs (const char * const *dirnames,
561                           const char * const *filebases,
562                           struct plugin_dir_handle *dirhandle,
563                           struct errinfo *ep)
564 {
565     long err = 0;
566     struct plugin_file_handle **h = NULL;
567     size_t count = 0;
568     char **filenames = NULL;
569     int i;
570
571     if (!err) {
572         err = krb5int_plugin_file_handle_array_init (&h);
573     }
574
575     if (!err && (filebases != NULL)) {
576         err = krb5int_get_plugin_filenames (filebases, &filenames);
577     }
578
579     for (i = 0; !err && dirnames[i] != NULL; i++) {
580         if (filenames != NULL) {
581             /* load plugins with names from filenames from each directory */
582             int j;
583
584             for (j = 0; !err && filenames[j] != NULL; j++) {
585                 struct plugin_file_handle *handle = NULL;
586                 char *filepath = NULL;
587
588                 if (!err) {
589                     if (asprintf(&filepath, "%s/%s", dirnames[i], filenames[j]) < 0) {
590                         filepath = NULL;
591                         err = ENOMEM;
592                     }
593                 }
594
595                 if (!err && krb5int_open_plugin(filepath, &handle, ep) == 0) {
596                     err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
597                     if (!err)
598                         handle = NULL; /* h takes ownership */
599                 }
600
601                 free(filepath);
602                 if (handle   != NULL) { krb5int_close_plugin (handle); }
603             }
604         } else {
605             /* load all plugins in each directory */
606             DIR *dir = opendir (dirnames[i]);
607
608             while (dir != NULL && !err) {
609                 struct dirent *d = NULL;
610                 char *filepath = NULL;
611                 struct plugin_file_handle *handle = NULL;
612
613                 d = readdir (dir);
614                 if (d == NULL) { break; }
615
616                 if ((strcmp (d->d_name, ".") == 0) ||
617                     (strcmp (d->d_name, "..") == 0)) {
618                     continue;
619                 }
620
621                 if (!err) {
622                     int len = NAMELEN (d);
623                     if (asprintf(&filepath, "%s/%*s", dirnames[i], len, d->d_name) < 0) {
624                         filepath = NULL;
625                         err = ENOMEM;
626                     }
627                 }
628
629                 if (!err) {
630                     if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
631                         err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
632                         if (!err) { handle = NULL; }  /* h takes ownership */
633                     }
634                 }
635
636                 free(filepath);
637                 if (handle    != NULL) { krb5int_close_plugin (handle); }
638             }
639
640             if (dir != NULL) { closedir (dir); }
641         }
642     }
643
644     if (err == ENOENT) {
645         err = 0;  /* ran out of plugins -- do nothing */
646     }
647
648     if (!err) {
649         dirhandle->files = h;
650         h = NULL;  /* dirhandle->files takes ownership */
651     }
652
653     if (filenames != NULL) { krb5int_free_plugin_filenames (filenames); }
654     if (h         != NULL) { krb5int_plugin_file_handle_array_free (h); }
655
656     return err;
657 }
658
659 void KRB5_CALLCONV
660 krb5int_close_plugin_dirs (struct plugin_dir_handle *dirhandle)
661 {
662     if (dirhandle->files != NULL) {
663         int i;
664         for (i = 0; dirhandle->files[i] != NULL; i++) {
665             krb5int_close_plugin (dirhandle->files[i]);
666         }
667         free (dirhandle->files);
668         dirhandle->files = NULL;
669     }
670 }
671
672 void KRB5_CALLCONV
673 krb5int_free_plugin_dir_data (void **ptrs)
674 {
675     /* Nothing special to be done per pointer.  */
676     free(ptrs);
677 }
678
679 long KRB5_CALLCONV
680 krb5int_get_plugin_dir_data (struct plugin_dir_handle *dirhandle,
681                              const char *symname,
682                              void ***ptrs,
683                              struct errinfo *ep)
684 {
685     long err = 0;
686     void **p = NULL;
687     size_t count = 0;
688
689     /* XXX Do we need to add a leading "_" to the symbol name on any
690        modern platforms?  */
691
692     Tprintf("get_plugin_data_sym(%s)\n", symname);
693
694     if (!err) {
695         p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
696         if (p == NULL) { err = ENOMEM; }
697     }
698
699     if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
700         int i = 0;
701
702         for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
703             void *sym = NULL;
704
705             if (krb5int_get_plugin_data (dirhandle->files[i], symname, &sym, ep) == 0) {
706                 void **newp = NULL;
707
708                 count++;
709                 newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
710                 if (newp == NULL) {
711                     err = ENOMEM;
712                 } else {
713                     p = newp;
714                     p[count - 1] = sym;
715                     p[count] = NULL;
716                 }
717             }
718         }
719     }
720
721     if (!err) {
722         *ptrs = p;
723         p = NULL; /* ptrs takes ownership */
724     }
725
726     free(p);
727
728     return err;
729 }
730
731 void KRB5_CALLCONV
732 krb5int_free_plugin_dir_func (void (**ptrs)(void))
733 {
734     /* Nothing special to be done per pointer.  */
735     free(ptrs);
736 }
737
738 long KRB5_CALLCONV
739 krb5int_get_plugin_dir_func (struct plugin_dir_handle *dirhandle,
740                              const char *symname,
741                              void (***ptrs)(void),
742                              struct errinfo *ep)
743 {
744     long err = 0;
745     void (**p)() = NULL;
746     size_t count = 0;
747
748     /* XXX Do we need to add a leading "_" to the symbol name on any
749        modern platforms?  */
750
751     Tprintf("get_plugin_data_sym(%s)\n", symname);
752
753     if (!err) {
754         p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
755         if (p == NULL) { err = ENOMEM; }
756     }
757
758     if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
759         int i = 0;
760
761         for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
762             void (*sym)() = NULL;
763
764             if (krb5int_get_plugin_func (dirhandle->files[i], symname, &sym, ep) == 0) {
765                 void (**newp)() = NULL;
766
767                 count++;
768                 newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
769                 if (newp == NULL) {
770                     err = ENOMEM;
771                 } else {
772                     p = newp;
773                     p[count - 1] = sym;
774                     p[count] = NULL;
775                 }
776             }
777         }
778     }
779
780     if (!err) {
781         *ptrs = p;
782         p = NULL; /* ptrs takes ownership */
783     }
784
785     free(p);
786
787     return err;
788 }