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