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