1 /*        $NetBSD: module.c,v 1.3 2021/08/14 16:14:58 christos Exp $  */
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2021 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: module.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
20 
21 #include "portable.h"
22 #include <stdio.h>
23 #include "slap.h"
24 
25 #ifdef SLAPD_MODULES
26 
27 #include <ltdl.h>
28 
29 typedef int (*MODULE_INIT_FN)(
30           int argc,
31           char *argv[]);
32 typedef int (*MODULE_LOAD_FN)(
33           const void *module,
34           const char *filename);
35 typedef int (*MODULE_TERM_FN)(void);
36 
37 
38 struct module_regtable_t {
39           char *type;
40           MODULE_LOAD_FN proc;
41 } module_regtable[] = {
42                     { "null", load_null_module },
43 #ifdef SLAPD_EXTERNAL_EXTENSIONS
44                     { "extension", load_extop_module },
45 #endif
46                     { NULL, NULL }
47 };
48 
49 typedef struct module_loaded_t {
50           struct module_loaded_t *next;
51           lt_dlhandle lib;
52           char name[1];
53 } module_loaded_t;
54 
55 module_loaded_t *module_list = NULL;
56 
57 static int module_int_unload (module_loaded_t *module);
58 
59 #ifdef HAVE_EBCDIC
60 static char ebuf[BUFSIZ];
61 #endif
62 
module_init(void)63 int module_init (void)
64 {
65           if (lt_dlinit()) {
66                     const char *error = lt_dlerror();
67 #ifdef HAVE_EBCDIC
68                     strcpy( ebuf, error );
69                     __etoa( ebuf );
70                     error = ebuf;
71 #endif
72                     Debug(LDAP_DEBUG_ANY, "lt_dlinit failed: %s\n", error );
73 
74                     return -1;
75           }
76 
77           return module_path( LDAP_MODULEDIR );
78 }
79 
module_kill(void)80 int module_kill (void)
81 {
82           /* unload all modules before shutdown */
83           while (module_list != NULL) {
84                     module_int_unload(module_list);
85           }
86 
87           if (lt_dlexit()) {
88                     const char *error = lt_dlerror();
89 #ifdef HAVE_EBCDIC
90                     strcpy( ebuf, error );
91                     __etoa( ebuf );
92                     error = ebuf;
93 #endif
94                     Debug(LDAP_DEBUG_ANY, "lt_dlexit failed: %s\n", error );
95 
96                     return -1;
97           }
98           return 0;
99 }
100 
module_handle(const char * file_name)101 void * module_handle( const char *file_name )
102 {
103           module_loaded_t *module;
104 
105           for ( module = module_list; module; module= module->next ) {
106                     if ( !strcmp( module->name, file_name )) {
107                               return module;
108                     }
109           }
110           return NULL;
111 }
112 
module_unload(const char * file_name)113 int module_unload( const char *file_name )
114 {
115           module_loaded_t *module;
116 
117           module = module_handle( file_name );
118           if ( module ) {
119                     module_int_unload( module );
120                     return 0;
121           }
122           return -1;          /* not found */
123 }
124 
module_load(const char * file_name,int argc,char * argv[])125 int module_load(const char* file_name, int argc, char *argv[])
126 {
127           module_loaded_t *module;
128           const char *error;
129           int rc;
130           MODULE_INIT_FN initialize;
131 #ifdef HAVE_EBCDIC
132 #define   file      ebuf
133 #else
134 #define   file      file_name
135 #endif
136 
137           module = module_handle( file_name );
138           if ( module ) {
139                     Debug( LDAP_DEBUG_ANY, "module_load: (%s) already loaded\n",
140                               file_name );
141                     return -1;
142           }
143 
144           /* If loading a backend, see if we already have it */
145           if ( !strncasecmp( file_name, "back_", 5 )) {
146                     char *name = (char *)file_name + 5;
147                     char *dot = strchr( name, '.');
148                     if (dot) *dot = '\0';
149                     rc = backend_info( name ) != NULL;
150                     if (dot) *dot = '.';
151                     if ( rc ) {
152                               Debug( LDAP_DEBUG_CONFIG, "module_load: (%s) already present (static)\n",
153                                         file_name );
154                               return 0;
155                     }
156           } else {
157                     /* check for overlays too */
158                     char *dot = strchr( file_name, '.' );
159                     if ( dot ) *dot = '\0';
160                     rc = overlay_find( file_name ) != NULL;
161                     if ( dot ) *dot = '.';
162                     if ( rc ) {
163                               Debug( LDAP_DEBUG_CONFIG, "module_load: (%s) already present (static)\n",
164                                         file_name );
165                               return 0;
166                     }
167           }
168 
169           module = (module_loaded_t *)ch_calloc(1, sizeof(module_loaded_t) +
170                     strlen(file_name));
171           if (module == NULL) {
172                     Debug(LDAP_DEBUG_ANY, "module_load failed: (%s) out of memory\n", file_name );
173 
174                     return -1;
175           }
176           strcpy( module->name, file_name );
177 
178 #ifdef HAVE_EBCDIC
179           strcpy( file, file_name );
180           __atoe( file );
181 #endif
182           /*
183            * The result of lt_dlerror(), when called, must be cached prior
184            * to calling Debug. This is because Debug is a macro that expands
185            * into multiple function calls.
186            */
187           if ((module->lib = lt_dlopenext(file)) == NULL) {
188                     error = lt_dlerror();
189 #ifdef HAVE_EBCDIC
190                     strcpy( ebuf, error );
191                     __etoa( ebuf );
192                     error = ebuf;
193 #endif
194                     Debug(LDAP_DEBUG_ANY, "lt_dlopenext failed: (%s) %s\n", file_name,
195                               error );
196 
197                     ch_free(module);
198                     return -1;
199           }
200 
201           Debug(LDAP_DEBUG_CONFIG, "loaded module %s\n", file_name );
202 
203 
204 #ifdef HAVE_EBCDIC
205 #pragma convlit(suspend)
206 #endif
207           if ((initialize = lt_dlsym(module->lib, "init_module")) == NULL) {
208 #ifdef HAVE_EBCDIC
209 #pragma convlit(resume)
210 #endif
211                     Debug(LDAP_DEBUG_CONFIG, "module %s: no init_module() function found\n",
212                               file_name );
213 
214                     lt_dlclose(module->lib);
215                     ch_free(module);
216                     return -1;
217           }
218 
219           /* The imported init_module() routine passes back the type of
220            * module (i.e., which part of slapd it should be hooked into)
221            * or -1 for error.  If it passes back 0, then you get the
222            * old behavior (i.e., the library is loaded and not hooked
223            * into anything).
224            *
225            * It might be better if the conf file could specify the type
226            * of module.  That way, a single module could support multiple
227            * type of hooks. This could be done by using something like:
228            *
229            *    moduleload extension /usr/local/openldap/whatever.so
230            *
231            * then we'd search through module_regtable for a matching
232            * module type, and hook in there.
233            */
234           rc = initialize(argc, argv);
235           if (rc == -1) {
236                     Debug(LDAP_DEBUG_CONFIG, "module %s: init_module() failed\n",
237                               file_name );
238 
239                     lt_dlclose(module->lib);
240                     ch_free(module);
241                     return rc;
242           }
243 
244           if (rc >= (int)(sizeof(module_regtable) / sizeof(struct module_regtable_t))
245                     || module_regtable[rc].proc == NULL)
246           {
247                     Debug(LDAP_DEBUG_CONFIG, "module %s: unknown registration type (%d)\n",
248                               file_name, rc );
249 
250                     module_int_unload(module);
251                     return -1;
252           }
253 
254           rc = (module_regtable[rc].proc)(module, file_name);
255           if (rc != 0) {
256                     Debug(LDAP_DEBUG_CONFIG, "module %s: %s module could not be registered\n",
257                               file_name, module_regtable[rc].type );
258 
259                     module_int_unload(module);
260                     return rc;
261           }
262 
263           module->next = module_list;
264           module_list = module;
265 
266           Debug(LDAP_DEBUG_CONFIG, "module %s: %s module registered\n",
267                     file_name, module_regtable[rc].type );
268 
269           return 0;
270 }
271 
module_path(const char * path)272 int module_path(const char *path)
273 {
274 #ifdef HAVE_EBCDIC
275           strcpy(ebuf, path);
276           __atoe(ebuf);
277           path = ebuf;
278 #endif
279           return lt_dlsetsearchpath( path );
280 }
281 
module_resolve(const void * module,const char * name)282 void *module_resolve (const void *module, const char *name)
283 {
284 #ifdef HAVE_EBCDIC
285           strcpy(ebuf, name);
286           __atoe(ebuf);
287           name = ebuf;
288 #endif
289           if (module == NULL || name == NULL)
290                     return(NULL);
291           return(lt_dlsym(((module_loaded_t *)module)->lib, name));
292 }
293 
module_int_unload(module_loaded_t * module)294 static int module_int_unload (module_loaded_t *module)
295 {
296           module_loaded_t *mod;
297           MODULE_TERM_FN terminate;
298 
299           if (module != NULL) {
300                     /* remove module from tracking list */
301                     if (module_list == module) {
302                               module_list = module->next;
303                     } else {
304                               for (mod = module_list; mod; mod = mod->next) {
305                                         if (mod->next == module) {
306                                                   mod->next = module->next;
307                                                   break;
308                                         }
309                               }
310                     }
311 
312                     /* call module's terminate routine, if present */
313 #ifdef HAVE_EBCDIC
314 #pragma convlit(suspend)
315 #endif
316                     if ((terminate = lt_dlsym(module->lib, "term_module"))) {
317 #ifdef HAVE_EBCDIC
318 #pragma convlit(resume)
319 #endif
320                               terminate();
321                     }
322 
323                     /* close the library and free the memory */
324                     lt_dlclose(module->lib);
325                     ch_free(module);
326           }
327           return 0;
328 }
329 
load_null_module(const void * module,const char * file_name)330 int load_null_module (const void *module, const char *file_name)
331 {
332           return 0;
333 }
334 
335 #ifdef SLAPD_EXTERNAL_EXTENSIONS
336 int
load_extop_module(const void * module,const char * file_name)337 load_extop_module (
338           const void *module,
339           const char *file_name
340 )
341 {
342           SLAP_EXTOP_MAIN_FN *ext_main;
343           SLAP_EXTOP_GETOID_FN *ext_getoid;
344           struct berval oid;
345           int rc;
346 
347           ext_main = (SLAP_EXTOP_MAIN_FN *)module_resolve(module, "ext_main");
348           if (ext_main == NULL) {
349                     return(-1);
350           }
351 
352           ext_getoid = module_resolve(module, "ext_getoid");
353           if (ext_getoid == NULL) {
354                     return(-1);
355           }
356 
357           rc = (ext_getoid)(0, &oid, 256);
358           if (rc != 0) {
359                     return(rc);
360           }
361           if (oid.bv_val == NULL || oid.bv_len == 0) {
362                     return(-1);
363           }
364 
365           /* FIXME: this is broken, and no longer needed,
366            * as a module can call load_extop() itself... */
367           rc = load_extop( &oid, ext_main );
368           return rc;
369 }
370 #endif /* SLAPD_EXTERNAL_EXTENSIONS */
371 #endif /* SLAPD_MODULES */
372 
373