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