xref: /freebsd/lib/libc/net/nsdispatch.c (revision b51f459a2098622c31ed54f5c1bf0e03efce403b)
1 /*	$NetBSD: nsdispatch.c,v 1.9 1999/01/25 00:16:17 lukem Exp $	*/
2 
3 /*-
4  * SPDX-License-Identifier: BSD-2-Clause-NetBSD
5  *
6  * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by Luke Mewburn.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 /*-
34  * Copyright (c) 2003 Networks Associates Technology, Inc.
35  * All rights reserved.
36  *
37  * Portions of this software were developed for the FreeBSD Project by
38  * Jacques A. Vidrine, Safeport Network Services, and Network
39  * Associates Laboratories, the Security Research Division of Network
40  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
41  * ("CBOSS"), as part of the DARPA CHATS research program.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  *
64  */
65 #include <sys/cdefs.h>
66 __FBSDID("$FreeBSD$");
67 
68 #include "namespace.h"
69 #include <sys/param.h>
70 #include <sys/stat.h>
71 
72 #include <assert.h>
73 #include <dlfcn.h>
74 #include <errno.h>
75 #include <fcntl.h>
76 #define _NS_PRIVATE
77 #include <nsswitch.h>
78 #include <pthread.h>
79 #include <pthread_np.h>
80 #include <stdatomic.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <syslog.h>
85 #include <unistd.h>
86 #include "un-namespace.h"
87 #include "nss_tls.h"
88 #include "libc_private.h"
89 #ifdef NS_CACHING
90 #include "nscache.h"
91 #endif
92 
93 enum _nss_constants {
94 	/* Number of elements allocated when we grow a vector */
95 	ELEMSPERCHUNK =	8
96 };
97 
98 /*
99  * Global NSS data structures are mostly read-only, but we update
100  * them when we read or re-read the nsswitch.conf.
101  */
102 static pthread_rwlock_t nss_lock = PTHREAD_RWLOCK_INITIALIZER;
103 #ifndef NDEBUG
104 static void *nss_wlock_owner __guarded_by(nss_lock);
105 #endif
106 
107 static inline /* __lock_annotate(locks_excluded(nss_lock)) */
108 __locks_exclusive(nss_lock) int
109 nss_wlock(void)
110 {
111 	int err;
112 
113 	err = _pthread_rwlock_wrlock(&nss_lock);
114 #ifndef NDEBUG
115 	assert(nss_wlock_owner == NULL);
116 	nss_wlock_owner = _pthread_self();
117 #endif
118 	assert(err == 0 && "Locking should not have failed!");
119 	return (err);
120 }
121 
122 /*
123  * XXX: The manpage says already held lock may result in EDEADLK, but
124  * actually libthr returns always EBUSY, so we need the extra owner
125  * variable for assertions.
126  */
127 #define ASSERT_NSS_WLOCK_HELD()                                                \
128 	do {                                                                   \
129 		if (__isthreaded) {                                            \
130 			assert(_pthread_rwlock_trywrlock(&nss_lock) == EBUSY); \
131 			assert(nss_wlock_owner == _pthread_self());            \
132 		}                                                              \
133 	} while (0)
134 
135 static inline __requires_exclusive(nss_lock) __unlocks(nss_lock) int
136 nss_wunlock(void)
137 {
138 	int err;
139 
140 	ASSERT_NSS_WLOCK_HELD();
141 #ifndef NDEBUG
142 	nss_wlock_owner = NULL;
143 #endif
144 	err = _pthread_rwlock_unlock(&nss_lock);
145 	assert(err == 0 && "Unlocking should not have failed!");
146 	return (err);
147 }
148 
149 /*
150  * Runtime determination of whether we are dynamically linked or not.
151  */
152 extern	int		_DYNAMIC __attribute__ ((weak));
153 #define	is_dynamic()	(&_DYNAMIC != NULL)
154 
155 /*
156  * default sourcelist: `files'
157  */
158 const ns_src __nsdefaultsrc[] = {
159 	{ NSSRC_FILES, NS_SUCCESS },
160 	{ 0 },
161 };
162 
163 /* Database, source mappings. */
164 static	unsigned int		 _nsmapsize __guarded_by(nss_lock);
165 static	ns_dbt			*_nsmap __guarded_by(nss_lock);
166 
167 /* NSS modules. */
168 static	unsigned int		 _nsmodsize __guarded_by(nss_lock);
169 static	ns_mod			*_nsmod __guarded_by(nss_lock);
170 
171 /* Placeholder for builtin modules' dlopen `handle'. */
172 static	int			 __nss_builtin_handle;
173 static	void			*nss_builtin_handle = &__nss_builtin_handle;
174 
175 #ifdef NS_CACHING
176 /*
177  * Cache lookup cycle prevention function - if !NULL then no cache lookups
178  * will be made
179  */
180 static	void			*nss_cache_cycle_prevention_func = NULL;
181 #endif
182 
183 /*
184  * We keep track of nsdispatch() nesting depth in dispatch_depth.  When a
185  * fallback method is invoked from nsdispatch(), we temporarily set
186  * fallback_depth to the current dispatch depth plus one.  Subsequent
187  * calls at that exact depth will run in fallback mode (restricted to the
188  * same source as the call that was handled by the fallback method), while
189  * calls below that depth will be handled normally, allowing fallback
190  * methods to perform arbitrary lookups.
191  */
192 struct fb_state {
193 	int	dispatch_depth;
194 	int	fallback_depth;
195 };
196 static	void	fb_endstate(void *);
197 NSS_TLS_HANDLING(fb);
198 
199 /*
200  * Attempt to spew relatively uniform messages to syslog.
201  */
202 #define nss_log(level, fmt, ...) \
203 	syslog((level), "NSSWITCH(%s): " fmt, __func__, __VA_ARGS__)
204 #define nss_log_simple(level, s) \
205 	syslog((level), "NSSWITCH(%s): " s, __func__)
206 
207 /*
208  * Dynamically growable arrays are used for lists of databases, sources,
209  * and modules.  The following `vector' interface is used to isolate the
210  * common operations.
211  */
212 typedef	int	(*vector_comparison)(const void *, const void *);
213 typedef	void	(*vector_free_elem)(void *);
214 static	void	  vector_sort(void *, unsigned int, size_t,
215 		    vector_comparison);
216 static	void	  _vector_free(void *, unsigned int, size_t, vector_free_elem);
217 static	void	 *vector_ref(unsigned int, void *, unsigned int, size_t);
218 static	void	 *vector_search(const void *, void *, unsigned int, size_t,
219 		    vector_comparison);
220 static	void	 *vector_append(const void *, void *, unsigned int *, size_t);
221 
222 
223 /*
224  * Internal interfaces.
225  */
226 static	int	 string_compare(const void *, const void *);
227 static	int	 mtab_compare(const void *, const void *);
228 static	int	 nss_configure(void);
229 static	void	 ns_dbt_free(ns_dbt *);
230 static	void	 ns_mod_free(ns_mod *);
231 static	void	 ns_src_free(ns_src **, int);
232 static	void	 nss_load_builtin_modules(void);
233 static	void	 nss_load_module(const char *, nss_module_register_fn);
234 static	void	 nss_atexit(void);
235 /* nsparser */
236 extern	FILE	*_nsyyin;
237 
238 
239 /*
240  * The vector operations
241  */
242 static void
243 vector_sort(void *vec, unsigned int count, size_t esize,
244     vector_comparison comparison)
245 {
246 	qsort(vec, count, esize, comparison);
247 }
248 
249 
250 static void *
251 vector_search(const void *key, void *vec, unsigned int count, size_t esize,
252     vector_comparison comparison)
253 {
254 	return (bsearch(key, vec, count, esize, comparison));
255 }
256 
257 
258 static void *
259 vector_append(const void *elem, void *vec, unsigned int *count, size_t esize)
260 {
261 	void	*p;
262 
263 	if ((*count % ELEMSPERCHUNK) == 0) {
264 		p = reallocarray(vec, *count + ELEMSPERCHUNK, esize);
265 		if (p == NULL) {
266 			nss_log_simple(LOG_ERR, "memory allocation failure");
267 			return (vec);
268 		}
269 		vec = p;
270 	}
271 	memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize);
272 	(*count)++;
273 	return (vec);
274 }
275 
276 
277 static void *
278 vector_ref(unsigned int i, void *vec, unsigned int count, size_t esize)
279 {
280 	if (i < count)
281 		return (void *)((uintptr_t)vec + (i * esize));
282 	else
283 		return (NULL);
284 }
285 
286 
287 #define VECTOR_FREE(vec, count, es, fe) do {	\
288 	_vector_free(vec, count, es, fe);	\
289 	vec = NULL;				\
290 	count = 0;				\
291 } while (0)
292 static void
293 _vector_free(void *vec, unsigned int count, size_t esize,
294     vector_free_elem free_elem)
295 {
296 	unsigned int	 i;
297 	void		*elem;
298 
299 	for (i = 0; i < count; i++) {
300 		elem = vector_ref(i, vec, count, esize);
301 		if (elem != NULL)
302 			free_elem(elem);
303 	}
304 	free(vec);
305 }
306 
307 /*
308  * Comparison functions for vector_search.
309  */
310 static int
311 string_compare(const void *a, const void *b)
312 {
313       return (strcasecmp(*(const char * const *)a, *(const char * const *)b));
314 }
315 
316 
317 static int
318 mtab_compare(const void *a, const void *b)
319 {
320       int     cmp;
321 
322       cmp = strcmp(((const ns_mtab *)a)->name, ((const ns_mtab *)b)->name);
323       if (cmp != 0)
324 	      return (cmp);
325       else
326 	      return (strcmp(((const ns_mtab *)a)->database,
327 		  ((const ns_mtab *)b)->database));
328 }
329 
330 /*
331  * NSS nsmap management.
332  */
333 __requires_exclusive(nss_lock) void
334 _nsdbtaddsrc(ns_dbt *dbt, const ns_src *src)
335 {
336 	const ns_mod	*modp;
337 
338 	ASSERT_NSS_WLOCK_HELD();
339 	dbt->srclist = vector_append(src, dbt->srclist,
340 	    (unsigned int *)&dbt->srclistsize, sizeof(*src));
341 	modp = vector_search(&src->name, _nsmod, _nsmodsize, sizeof(*_nsmod),
342 	    string_compare);
343 	if (modp == NULL)
344 		nss_load_module(src->name, NULL);
345 }
346 
347 
348 #ifdef _NSS_DEBUG
349 void
350 _nsdbtdump(const ns_dbt *dbt)
351 {
352 	int i;
353 
354 	printf("%s (%d source%s):", dbt->name, dbt->srclistsize,
355 	    dbt->srclistsize == 1 ? "" : "s");
356 	for (i = 0; i < (int)dbt->srclistsize; i++) {
357 		printf(" %s", dbt->srclist[i].name);
358 		if (!(dbt->srclist[i].flags &
359 		    (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) &&
360 		    (dbt->srclist[i].flags & NS_SUCCESS))
361 			continue;
362 		printf(" [");
363 		if (!(dbt->srclist[i].flags & NS_SUCCESS))
364 			printf(" SUCCESS=continue");
365 		if (dbt->srclist[i].flags & NS_UNAVAIL)
366 			printf(" UNAVAIL=return");
367 		if (dbt->srclist[i].flags & NS_NOTFOUND)
368 			printf(" NOTFOUND=return");
369 		if (dbt->srclist[i].flags & NS_TRYAGAIN)
370 			printf(" TRYAGAIN=return");
371 		printf(" ]");
372 	}
373 	printf("\n");
374 }
375 #endif
376 
377 #ifndef NS_REREAD_CONF
378 static int __guarded_by(nss_lock) already_initialized = 0;
379 #endif
380 
381 /*
382  * The first time nsdispatch is called (during a process's lifetime,
383  * or after nsswitch.conf has been updated), nss_configure will
384  * prepare global data needed by NSS.
385  */
386 static __requires_shared(nss_lock) int
387 __lock_annotate(no_thread_safety_analysis) /* RWLock upgrade not supported. */
388 do_nss_configure(void)
389 {
390 	static time_t __guarded_by(nss_lock) confmod = 0;
391 	struct stat	 statbuf;
392 	int		 result;
393 	const char	*path;
394 #ifdef NS_CACHING
395 	void		*handle;
396 #endif
397 
398 	result = 0;
399 #if defined(_NSS_DEBUG) && defined(_NSS_SHOOT_FOOT)
400 	/* NOTE WELL:  THIS IS A SECURITY HOLE. This must only be built
401 	 * for debugging purposes and MUST NEVER be used in production.
402 	 */
403 	path = getenv("NSSWITCH_CONF");
404 	if (path == NULL)
405 #endif
406 		path = _PATH_NS_CONF;
407 
408 	result = _pthread_rwlock_unlock(&nss_lock);
409 	if (result != 0)
410 		return (result);
411 	result = nss_wlock();
412 	if (result != 0)
413 		return (result);
414 #ifndef NS_REREAD_CONF
415 	/*
416 	 * Another thread could have already run the initialization
417 	 * logic while we were waiting for the write lock. Check
418 	 * already_initialized to avoid re-initializing.
419 	 */
420 	if (already_initialized)
421 		goto fin;
422 #endif /* NS_REREAD_CONF */
423 	ASSERT_NSS_WLOCK_HELD();
424 	if (stat(path, &statbuf) != 0)
425 		goto fin;
426 	if (statbuf.st_mtime <= confmod)
427 		goto fin;
428 	_nsyyin = fopen(path, "re");
429 	if (_nsyyin == NULL)
430 		goto fin;
431 	VECTOR_FREE(_nsmap, _nsmapsize, sizeof(*_nsmap),
432 	    (vector_free_elem)ns_dbt_free);
433 	VECTOR_FREE(_nsmod, _nsmodsize, sizeof(*_nsmod),
434 	    (vector_free_elem)ns_mod_free);
435 	if (confmod == 0)
436 		(void)atexit(nss_atexit);
437 	nss_load_builtin_modules();
438 	_nsyyparse();
439 	(void)fclose(_nsyyin);
440 	vector_sort(_nsmap, _nsmapsize, sizeof(*_nsmap), string_compare);
441 	confmod = statbuf.st_mtime;
442 
443 #ifdef NS_CACHING
444 	handle = libc_dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
445 	if (handle != NULL) {
446 		nss_cache_cycle_prevention_func = dlsym(handle,
447 		    "_nss_cache_cycle_prevention_function");
448 		dlclose(handle);
449 	}
450 #endif
451 #ifndef NS_REREAD_CONF
452 	already_initialized = 1;
453 #endif
454 fin:
455 	result = nss_wunlock();
456 	if (result == 0)
457 		result = _pthread_rwlock_rdlock(&nss_lock);
458 	return (result);
459 }
460 
461 static __requires_shared(nss_lock) int
462 nss_configure(void)
463 {
464 	int result;
465 #ifndef NS_REREAD_CONF
466 	/*
467 	 * Define NS_REREAD_CONF to have nsswitch notice changes
468 	 * to nsswitch.conf(5) during runtime.  This involves calling
469 	 * stat(2) every time, which can result in performance hit.
470 	 */
471 	if (already_initialized)
472 		return (0);
473 #endif /* NS_REREAD_CONF */
474 	result = do_nss_configure();
475 	return (result);
476 }
477 
478 
479 __requires_exclusive(nss_lock) void
480 _nsdbtput(const ns_dbt *dbt)
481 {
482 	unsigned int	 i;
483 	ns_dbt		*p;
484 
485 	ASSERT_NSS_WLOCK_HELD();
486 
487 	for (i = 0; i < _nsmapsize; i++) {
488 		p = vector_ref(i, _nsmap, _nsmapsize, sizeof(*_nsmap));
489 		if (string_compare(&dbt->name, &p->name) == 0) {
490 			/* overwrite existing entry */
491 			if (p->srclist != NULL)
492 				ns_src_free(&p->srclist, p->srclistsize);
493 			memmove(p, dbt, sizeof(*dbt));
494 			return;
495 		}
496 	}
497 	_nsmap = vector_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap));
498 }
499 
500 
501 static void
502 ns_dbt_free(ns_dbt *dbt)
503 {
504 	ns_src_free(&dbt->srclist, dbt->srclistsize);
505 	if (dbt->name)
506 		free((void *)dbt->name);
507 }
508 
509 
510 static void
511 ns_src_free(ns_src **src, int srclistsize)
512 {
513 	int	i;
514 
515 	for (i = 0; i < srclistsize; i++)
516 		if ((*src)[i].name != NULL)
517 			/* This one was allocated by nslexer. You'll just
518 			 * have to trust me.
519 			 */
520 			free((void *)((*src)[i].name));
521 	free(*src);
522 	*src = NULL;
523 }
524 
525 
526 
527 /*
528  * NSS module management.
529  */
530 /* The built-in NSS modules are all loaded at once. */
531 #define NSS_BACKEND(name, reg) \
532 ns_mtab	*reg(unsigned int *, nss_module_unregister_fn *);
533 #include "nss_backends.h"
534 #undef NSS_BACKEND
535 
536 static void
537 nss_load_builtin_modules(void)
538 {
539 #define NSS_BACKEND(name, reg) nss_load_module(#name, reg);
540 #include "nss_backends.h"
541 #undef NSS_BACKEND
542 }
543 
544 
545 /* Load a built-in or dynamically linked module.  If the `reg_fn'
546  * argument is non-NULL, assume a built-in module and use reg_fn to
547  * register it.  Otherwise, search for a dynamic NSS module.
548  */
549 static __requires_exclusive(nss_lock) void
550 nss_load_module(const char *source, nss_module_register_fn reg_fn)
551 {
552 	char		 buf[PATH_MAX];
553 	ns_mod		 mod;
554 	nss_module_register_fn fn;
555 
556 	ASSERT_NSS_WLOCK_HELD();
557 
558 	memset(&mod, 0, sizeof(mod));
559 	mod.name = strdup(source);
560 	if (mod.name == NULL) {
561 		nss_log_simple(LOG_ERR, "memory allocation failure");
562 		return;
563 	}
564 	if (reg_fn != NULL) {
565 		/* The placeholder is required, as a NULL handle
566 		 * represents an invalid module.
567 		 */
568 		mod.handle = nss_builtin_handle;
569 		fn = reg_fn;
570 	} else if (!is_dynamic()) {
571 		goto fin;
572 	} else if (strcmp(source, NSSRC_CACHE) == 0 ||
573 	    strcmp(source, NSSRC_COMPAT) == 0 ||
574 	    strcmp(source, NSSRC_DB) == 0 ||
575 	    strcmp(source, NSSRC_DNS) == 0 ||
576 	    strcmp(source, NSSRC_FILES) == 0 ||
577 	    strcmp(source, NSSRC_NIS) == 0) {
578 		/*
579 		 * Avoid calling dlopen(3) for built-in modules.
580 		 */
581 		goto fin;
582 	} else {
583 		if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name,
584 		    NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf))
585 			goto fin;
586 		mod.handle = libc_dlopen(buf, RTLD_LOCAL|RTLD_LAZY);
587 		if (mod.handle == NULL) {
588 #ifdef _NSS_DEBUG
589 			/* This gets pretty annoying since the built-in
590 			 * sources aren't modules yet.
591 			 */
592 			nss_log(LOG_DEBUG, "%s, %s", mod.name, dlerror());
593 #endif
594 			goto fin;
595 		}
596 		fn = (nss_module_register_fn)dlfunc(mod.handle,
597 		    "nss_module_register");
598 		if (fn == NULL) {
599 			(void)dlclose(mod.handle);
600 			mod.handle = NULL;
601 			nss_log(LOG_ERR, "%s, %s", mod.name, dlerror());
602 			goto fin;
603 		}
604 	}
605 	mod.mtab = fn(mod.name, &mod.mtabsize, &mod.unregister);
606 	if (mod.mtab == NULL || mod.mtabsize == 0) {
607 		if (mod.handle != nss_builtin_handle)
608 			(void)dlclose(mod.handle);
609 		mod.handle = NULL;
610 		nss_log(LOG_ERR, "%s, registration failed", mod.name);
611 		goto fin;
612 	}
613 	if (mod.mtabsize > 1)
614 		qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]),
615 		    mtab_compare);
616 fin:
617 	_nsmod = vector_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod));
618 	vector_sort(_nsmod, _nsmodsize, sizeof(*_nsmod), string_compare);
619 }
620 
621 static int exiting __guarded_by(nss_lock) = 0;
622 
623 static __requires_exclusive(nss_lock) void
624 ns_mod_free(ns_mod *mod)
625 {
626 
627 	free(mod->name);
628 	if (mod->handle == NULL)
629 		return;
630 	if (mod->unregister != NULL)
631 		mod->unregister(mod->mtab, mod->mtabsize);
632 	if (mod->handle != nss_builtin_handle && !exiting)
633 		(void)dlclose(mod->handle);
634 }
635 
636 /*
637  * Cleanup
638  */
639 static void
640 nss_atexit(void)
641 {
642 	(void)nss_wlock();
643 	exiting = 1;
644 	VECTOR_FREE(_nsmap, _nsmapsize, sizeof(*_nsmap),
645 	    (vector_free_elem)ns_dbt_free);
646 	VECTOR_FREE(_nsmod, _nsmapsize, sizeof(*_nsmod),
647 	    (vector_free_elem)ns_mod_free);
648 	(void)nss_wunlock();
649 }
650 
651 /*
652  * Finally, the actual implementation.
653  */
654 static __requires_shared(nss_lock) nss_method
655 nss_method_lookup(const char *source, const char *database,
656     const char *method, const ns_dtab disp_tab[], void **mdata)
657 {
658 	ns_mod	*mod;
659 	ns_mtab	*match, key;
660 	int	 i;
661 
662 	if (disp_tab != NULL)
663 		for (i = 0; disp_tab[i].src != NULL; i++)
664 			if (strcasecmp(source, disp_tab[i].src) == 0) {
665 				*mdata = disp_tab[i].mdata;
666 				return (disp_tab[i].method);
667 			}
668 	mod = vector_search(&source, _nsmod, _nsmodsize, sizeof(*_nsmod),
669 	    string_compare);
670 	if (mod != NULL && mod->handle != NULL) {
671 		key.database = database;
672 		key.name = method;
673 		match = bsearch(&key, mod->mtab, mod->mtabsize,
674 		    sizeof(mod->mtab[0]), mtab_compare);
675 		if (match != NULL) {
676 			*mdata = match->mdata;
677 			return (match->method);
678 		}
679 	}
680 
681 	*mdata = NULL;
682 	return (NULL);
683 }
684 
685 static void
686 fb_endstate(void *p)
687 {
688 	free(p);
689 }
690 
691 __weak_reference(_nsdispatch, nsdispatch);
692 
693 __requires_unlocked(nss_lock) int
694 _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
695 	    const char *method_name, const ns_src defaults[], ...)
696 {
697 	va_list		 ap;
698 	const ns_dbt	*dbt;
699 	const ns_src	*srclist;
700 	nss_method	 method, fb_method;
701 	void		*mdata;
702 	int		serrno, i, result, srclistsize;
703 	struct fb_state	*st;
704 	int		 saved_depth;
705 
706 #ifdef NS_CACHING
707 	nss_cache_data	 cache_data;
708 	nss_cache_data	*cache_data_p;
709 	int		 cache_flag;
710 #endif
711 
712 	dbt = NULL;
713 	fb_method = NULL;
714 
715 	serrno = errno;
716 	(void)_pthread_rwlock_rdlock(&nss_lock);
717 
718 	result = fb_getstate(&st);
719 	if (result != 0) {
720 		result = NS_UNAVAIL;
721 		goto fin;
722 	}
723 
724 	result = nss_configure();
725 	if (result != 0) {
726 		result = NS_UNAVAIL;
727 		goto fin;
728 	}
729 	++st->dispatch_depth;
730 	if (st->dispatch_depth > st->fallback_depth) {
731 		dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap),
732 		    string_compare);
733 		fb_method = nss_method_lookup(NSSRC_FALLBACK, database,
734 		    method_name, disp_tab, &mdata);
735 	}
736 
737 	if (dbt != NULL) {
738 		srclist = dbt->srclist;
739 		srclistsize = dbt->srclistsize;
740 	} else {
741 		srclist = defaults;
742 		srclistsize = 0;
743 		while (srclist[srclistsize].name != NULL)
744 			srclistsize++;
745 	}
746 
747 #ifdef NS_CACHING
748 	cache_data_p = NULL;
749 	cache_flag = 0;
750 #endif
751 	for (i = 0; i < srclistsize; i++) {
752 		result = NS_NOTFOUND;
753 		method = nss_method_lookup(srclist[i].name, database,
754 		    method_name, disp_tab, &mdata);
755 
756 		if (method != NULL) {
757 #ifdef NS_CACHING
758 			if (strcmp(srclist[i].name, NSSRC_CACHE) == 0 &&
759 			    nss_cache_cycle_prevention_func == NULL) {
760 #ifdef NS_STRICT_LIBC_EID_CHECKING
761 				if (issetugid() != 0)
762 					continue;
763 #endif
764 				cache_flag = 1;
765 
766 				memset(&cache_data, 0, sizeof(nss_cache_data));
767 				cache_data.info = (nss_cache_info const *)mdata;
768 				cache_data_p = &cache_data;
769 
770 				va_start(ap, defaults);
771 				if (cache_data.info->id_func != NULL)
772 					result = __nss_common_cache_read(retval,
773 					    cache_data_p, ap);
774 				else if (cache_data.info->marshal_func != NULL)
775 					result = __nss_mp_cache_read(retval,
776 					    cache_data_p, ap);
777 				else
778 					result = __nss_mp_cache_end(retval,
779 					    cache_data_p, ap);
780 				va_end(ap);
781 			} else {
782 				cache_flag = 0;
783 				errno = 0;
784 				va_start(ap, defaults);
785 				result = method(retval, mdata, ap);
786 				va_end(ap);
787 			}
788 #else /* NS_CACHING */
789 			errno = 0;
790 			va_start(ap, defaults);
791 			result = method(retval, mdata, ap);
792 			va_end(ap);
793 #endif /* NS_CACHING */
794 
795 			if (result & (srclist[i].flags))
796 				break;
797 		} else {
798 			if (fb_method != NULL) {
799 				saved_depth = st->fallback_depth;
800 				st->fallback_depth = st->dispatch_depth + 1;
801 				va_start(ap, defaults);
802 				result = fb_method(retval,
803 				    (void *)srclist[i].name, ap);
804 				va_end(ap);
805 				st->fallback_depth = saved_depth;
806 			} else
807 				nss_log(LOG_DEBUG, "%s, %s, %s, not found, "
808 				    "and no fallback provided",
809 				    srclist[i].name, database, method_name);
810 		}
811 	}
812 
813 #ifdef NS_CACHING
814 	if (cache_data_p != NULL &&
815 	    (result & (NS_NOTFOUND | NS_SUCCESS)) && cache_flag == 0) {
816 		va_start(ap, defaults);
817 		if (result == NS_SUCCESS) {
818 			if (cache_data.info->id_func != NULL)
819 				__nss_common_cache_write(retval, cache_data_p,
820 				    ap);
821 			else if (cache_data.info->marshal_func != NULL)
822 				__nss_mp_cache_write(retval, cache_data_p, ap);
823 		} else if (result == NS_NOTFOUND) {
824 			if (cache_data.info->id_func == NULL) {
825 				if (cache_data.info->marshal_func != NULL)
826 					__nss_mp_cache_write_submit(retval,
827 					    cache_data_p, ap);
828 			} else
829 				__nss_common_cache_write_negative(cache_data_p);
830 		}
831 		va_end(ap);
832 	}
833 #endif /* NS_CACHING */
834 
835 	--st->dispatch_depth;
836 fin:
837 	(void)_pthread_rwlock_unlock(&nss_lock);
838 	errno = serrno;
839 	return (result);
840 }
841