1 1.39 rillig /* $NetBSD: nsdispatch.c,v 1.39 2022/04/19 20:32:15 rillig Exp $ */ 2 1.2 lukem 3 1.2 lukem /*- 4 1.22 thorpej * Copyright (c) 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc. 5 1.2 lukem * All rights reserved. 6 1.2 lukem * 7 1.2 lukem * This code is derived from software contributed to The NetBSD Foundation 8 1.22 thorpej * by Luke Mewburn; and by Jason R. Thorpe. 9 1.2 lukem * 10 1.2 lukem * Redistribution and use in source and binary forms, with or without 11 1.2 lukem * modification, are permitted provided that the following conditions 12 1.2 lukem * are met: 13 1.2 lukem * 1. Redistributions of source code must retain the above copyright 14 1.2 lukem * notice, this list of conditions and the following disclaimer. 15 1.2 lukem * 2. Redistributions in binary form must reproduce the above copyright 16 1.2 lukem * notice, this list of conditions and the following disclaimer in the 17 1.2 lukem * documentation and/or other materials provided with the distribution. 18 1.2 lukem * 19 1.2 lukem * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.2 lukem * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.2 lukem * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.2 lukem * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.2 lukem * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.2 lukem * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.2 lukem * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.2 lukem * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.2 lukem * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.2 lukem * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.2 lukem * POSSIBILITY OF SUCH DAMAGE. 30 1.2 lukem */ 31 1.9 lukem 32 1.22 thorpej /*- 33 1.22 thorpej * Copyright (c) 2003 Networks Associates Technology, Inc. 34 1.22 thorpej * All rights reserved. 35 1.22 thorpej * 36 1.22 thorpej * Portions of this software were developed for the FreeBSD Project by 37 1.22 thorpej * Jacques A. Vidrine, Safeport Network Services, and Network 38 1.22 thorpej * Associates Laboratories, the Security Research Division of Network 39 1.22 thorpej * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 40 1.22 thorpej * ("CBOSS"), as part of the DARPA CHATS research program. 41 1.22 thorpej * 42 1.22 thorpej * Redistribution and use in source and binary forms, with or without 43 1.22 thorpej * modification, are permitted provided that the following conditions 44 1.22 thorpej * are met: 45 1.22 thorpej * 1. Redistributions of source code must retain the above copyright 46 1.22 thorpej * notice, this list of conditions and the following disclaimer. 47 1.22 thorpej * 2. Redistributions in binary form must reproduce the above copyright 48 1.22 thorpej * notice, this list of conditions and the following disclaimer in the 49 1.22 thorpej * documentation and/or other materials provided with the distribution. 50 1.22 thorpej * 51 1.22 thorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 52 1.22 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 1.22 thorpej * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 1.22 thorpej * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 55 1.22 thorpej * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 1.22 thorpej * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 1.22 thorpej * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 1.22 thorpej * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 1.22 thorpej * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 1.22 thorpej * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 1.22 thorpej * SUCH DAMAGE. 62 1.22 thorpej */ 63 1.22 thorpej 64 1.9 lukem #include <sys/cdefs.h> 65 1.9 lukem #if defined(LIBC_SCCS) && !defined(lint) 66 1.39 rillig __RCSID("$NetBSD: nsdispatch.c,v 1.39 2022/04/19 20:32:15 rillig Exp $"); 67 1.9 lukem #endif /* LIBC_SCCS and not lint */ 68 1.2 lukem 69 1.6 lukem #include "namespace.h" 70 1.6 lukem 71 1.2 lukem #include <sys/types.h> 72 1.2 lukem #include <sys/param.h> 73 1.2 lukem #include <sys/stat.h> 74 1.23 thorpej #include <sys/queue.h> 75 1.2 lukem 76 1.12 lukem #include <assert.h> 77 1.22 thorpej #ifdef __ELF__ 78 1.22 thorpej #include <dlfcn.h> 79 1.22 thorpej #endif /* __ELF__ */ 80 1.2 lukem #include <err.h> 81 1.2 lukem #include <fcntl.h> 82 1.2 lukem #define _NS_PRIVATE 83 1.2 lukem #include <nsswitch.h> 84 1.18 wiz #include <stdarg.h> 85 1.2 lukem #include <stdio.h> 86 1.2 lukem #include <stdlib.h> 87 1.2 lukem #include <string.h> 88 1.2 lukem #include <unistd.h> 89 1.22 thorpej 90 1.22 thorpej #include "reentrant.h" 91 1.10 lukem 92 1.17 christos extern FILE *_nsyyin; 93 1.21 thorpej extern int _nsyyparse(void); 94 1.17 christos 95 1.17 christos 96 1.6 lukem #ifdef __weak_alias 97 1.16 mycroft __weak_alias(nsdispatch,_nsdispatch) 98 1.6 lukem #endif 99 1.2 lukem 100 1.2 lukem 101 1.5 lukem /* 102 1.5 lukem * default sourcelist: `files' 103 1.5 lukem */ 104 1.5 lukem const ns_src __nsdefaultsrc[] = { 105 1.28 lukem { NSSRC_FILES, NS_SUCCESS }, 106 1.31 christos { 0, 0 }, 107 1.5 lukem }; 108 1.5 lukem 109 1.28 lukem const ns_src __nsdefaultcompat[] = { 110 1.28 lukem { NSSRC_COMPAT, NS_SUCCESS }, 111 1.31 christos { 0, 0 } 112 1.28 lukem }; 113 1.28 lukem 114 1.28 lukem const ns_src __nsdefaultcompat_forceall[] = { 115 1.28 lukem { NSSRC_COMPAT, NS_SUCCESS | NS_FORCEALL }, 116 1.31 christos { 0, 0 } 117 1.28 lukem }; 118 1.28 lukem 119 1.28 lukem const ns_src __nsdefaultfiles[] = { 120 1.28 lukem { NSSRC_FILES, NS_SUCCESS }, 121 1.31 christos { 0, 0 }, 122 1.28 lukem }; 123 1.28 lukem 124 1.28 lukem const ns_src __nsdefaultfiles_forceall[] = { 125 1.28 lukem { NSSRC_FILES, NS_SUCCESS | NS_FORCEALL }, 126 1.31 christos { 0, 0 }, 127 1.28 lukem }; 128 1.28 lukem 129 1.28 lukem const ns_src __nsdefaultnis[] = { 130 1.28 lukem { NSSRC_NIS, NS_SUCCESS }, 131 1.31 christos { 0, 0 } 132 1.28 lukem }; 133 1.28 lukem 134 1.28 lukem const ns_src __nsdefaultnis_forceall[] = { 135 1.28 lukem { NSSRC_NIS, NS_SUCCESS | NS_FORCEALL }, 136 1.31 christos { 0, 0 } 137 1.28 lukem }; 138 1.28 lukem 139 1.28 lukem 140 1.22 thorpej /* Database, source mappings. */ 141 1.22 thorpej static u_int _nsmapsize; 142 1.22 thorpej static ns_dbt *_nsmap; 143 1.22 thorpej 144 1.22 thorpej /* Nsswitch modules. */ 145 1.22 thorpej static u_int _nsmodsize; 146 1.22 thorpej static ns_mod *_nsmod; 147 1.22 thorpej 148 1.22 thorpej /* Placeholder for built-in modules' dlopen() handles. */ 149 1.22 thorpej static void *_nsbuiltin = &_nsbuiltin; 150 1.5 lukem 151 1.19 christos #ifdef _REENTRANT 152 1.22 thorpej /* 153 1.22 thorpej * Global nsswitch data structures are mostly read-only, but we update them 154 1.22 thorpej * when we read or re-read nsswitch.conf. 155 1.22 thorpej */ 156 1.22 thorpej static rwlock_t _nslock = RWLOCK_INITIALIZER; 157 1.23 thorpej 158 1.23 thorpej /* 159 1.23 thorpej * List of threads currently in nsdispatch(). We use this to detect 160 1.23 thorpej * recursive calls and avoid reloading configuration in such cases, 161 1.23 thorpej * which could cause deadlock. 162 1.23 thorpej */ 163 1.23 thorpej struct _ns_drec { 164 1.23 thorpej LIST_ENTRY(_ns_drec) list; 165 1.23 thorpej thr_t thr; 166 1.23 thorpej }; 167 1.23 thorpej static LIST_HEAD(, _ns_drec) _ns_drec = LIST_HEAD_INITIALIZER(&_ns_drec); 168 1.23 thorpej static mutex_t _ns_drec_lock = MUTEX_INITIALIZER; 169 1.23 thorpej #endif /* _REENTRANT */ 170 1.22 thorpej 171 1.22 thorpej 172 1.22 thorpej /* 173 1.22 thorpej * Runtime determination of whether we are dynamically linked or not. 174 1.22 thorpej */ 175 1.35 joerg #ifndef __ELF__ 176 1.35 joerg #define is_dynamic() (0) /* don't bother - switch to ELF! */ 177 1.36 joerg #else 178 1.36 joerg __weakref_visible int rtld_DYNAMIC __weak_reference(_DYNAMIC); 179 1.35 joerg #define is_dynamic() (&rtld_DYNAMIC != NULL) 180 1.35 joerg #endif 181 1.19 christos 182 1.2 lukem 183 1.2 lukem /* 184 1.22 thorpej * size of dynamic array chunk for _nsmap and _nsmap[x].srclist (and other 185 1.22 thorpej * growing arrays). 186 1.2 lukem */ 187 1.2 lukem #define NSELEMSPERCHUNK 8 188 1.2 lukem 189 1.22 thorpej /* 190 1.22 thorpej * Dynamically growable arrays are used for lists of databases, sources, 191 1.22 thorpej * and modules. The following "vector" API is used to isolate the 192 1.22 thorpej * common operations. 193 1.22 thorpej */ 194 1.22 thorpej typedef void (*_nsvect_free_elem)(void *); 195 1.22 thorpej 196 1.22 thorpej static void * 197 1.22 thorpej _nsvect_append(const void *elem, void *vec, u_int *count, size_t esize) 198 1.22 thorpej { 199 1.22 thorpej void *p; 200 1.22 thorpej 201 1.22 thorpej if ((*count % NSELEMSPERCHUNK) == 0) { 202 1.22 thorpej p = realloc(vec, (*count + NSELEMSPERCHUNK) * esize); 203 1.22 thorpej if (p == NULL) 204 1.22 thorpej return (NULL); 205 1.22 thorpej vec = p; 206 1.22 thorpej } 207 1.22 thorpej memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize); 208 1.22 thorpej (*count)++; 209 1.22 thorpej return (vec); 210 1.22 thorpej } 211 1.22 thorpej 212 1.22 thorpej static void * 213 1.22 thorpej _nsvect_elem(u_int i, void *vec, u_int count, size_t esize) 214 1.22 thorpej { 215 1.22 thorpej 216 1.22 thorpej if (i < count) 217 1.22 thorpej return ((void *)((uintptr_t)vec + (i * esize))); 218 1.22 thorpej else 219 1.22 thorpej return (NULL); 220 1.22 thorpej } 221 1.22 thorpej 222 1.22 thorpej static void 223 1.22 thorpej _nsvect_free(void *vec, u_int *count, size_t esize, _nsvect_free_elem free_elem) 224 1.22 thorpej { 225 1.22 thorpej void *elem; 226 1.22 thorpej u_int i; 227 1.22 thorpej 228 1.22 thorpej for (i = 0; i < *count; i++) { 229 1.22 thorpej elem = _nsvect_elem(i, vec, *count, esize); 230 1.22 thorpej if (elem != NULL) 231 1.22 thorpej (*free_elem)(elem); 232 1.22 thorpej } 233 1.22 thorpej if (vec != NULL) 234 1.22 thorpej free(vec); 235 1.22 thorpej *count = 0; 236 1.22 thorpej } 237 1.22 thorpej #define _NSVECT_FREE(v, c, s, f) \ 238 1.22 thorpej do { \ 239 1.22 thorpej _nsvect_free((v), (c), (s), (f)); \ 240 1.22 thorpej (v) = NULL; \ 241 1.39 rillig } while (0) 242 1.2 lukem 243 1.21 thorpej static int 244 1.22 thorpej _nsdbtcmp(const void *a, const void *b) 245 1.2 lukem { 246 1.22 thorpej 247 1.7 christos return (strcasecmp(((const ns_dbt *)a)->name, 248 1.7 christos ((const ns_dbt *)b)->name)); 249 1.2 lukem } 250 1.2 lukem 251 1.22 thorpej static int 252 1.22 thorpej _nsmodcmp(const void *a, const void *b) 253 1.22 thorpej { 254 1.22 thorpej 255 1.22 thorpej return (strcasecmp(((const ns_mod *)a)->name, 256 1.22 thorpej ((const ns_mod *)b)->name)); 257 1.22 thorpej } 258 1.22 thorpej 259 1.22 thorpej static int 260 1.22 thorpej _nsmtabcmp(const void *a, const void *b) 261 1.22 thorpej { 262 1.22 thorpej int cmp; 263 1.22 thorpej 264 1.22 thorpej cmp = strcmp(((const ns_mtab *)a)->name, 265 1.22 thorpej ((const ns_mtab *)b)->name); 266 1.22 thorpej if (cmp) 267 1.22 thorpej return (cmp); 268 1.22 thorpej 269 1.22 thorpej return (strcasecmp(((const ns_mtab *)a)->database, 270 1.22 thorpej ((const ns_mtab *)b)->database)); 271 1.22 thorpej } 272 1.22 thorpej 273 1.22 thorpej static void 274 1.22 thorpej _nsmodfree(ns_mod *mod) 275 1.22 thorpej { 276 1.22 thorpej 277 1.30 christos free(__UNCONST(mod->name)); 278 1.22 thorpej if (mod->handle == NULL) 279 1.22 thorpej return; 280 1.22 thorpej if (mod->unregister != NULL) 281 1.22 thorpej (*mod->unregister)(mod->mtab, mod->mtabsize); 282 1.22 thorpej #ifdef __ELF__ 283 1.22 thorpej if (mod->handle != _nsbuiltin) 284 1.22 thorpej (void) dlclose(mod->handle); 285 1.22 thorpej #endif /* __ELF__ */ 286 1.22 thorpej } 287 1.22 thorpej 288 1.22 thorpej /* 289 1.22 thorpej * Load a built-in or dyanamically linked module. If the `reg_fn' 290 1.22 thorpej * argument is non-NULL, assume a built-in module and use `reg_fn' 291 1.22 thorpej * to register it. Otherwise, search for a dynamic nsswitch module. 292 1.22 thorpej */ 293 1.22 thorpej static int 294 1.22 thorpej _nsloadmod(const char *source, nss_module_register_fn reg_fn) 295 1.22 thorpej { 296 1.24 simonb #ifdef __ELF__ 297 1.22 thorpej char buf[PATH_MAX]; 298 1.24 simonb #endif 299 1.22 thorpej ns_mod mod, *new; 300 1.22 thorpej 301 1.22 thorpej memset(&mod, 0, sizeof(mod)); 302 1.22 thorpej mod.name = strdup(source); 303 1.22 thorpej if (mod.name == NULL) 304 1.22 thorpej return (-1); 305 1.22 thorpej 306 1.22 thorpej if (reg_fn != NULL) { 307 1.22 thorpej /* 308 1.22 thorpej * The placeholder is required, as a NULL handle 309 1.22 thorpej * represents an invalid module. 310 1.22 thorpej */ 311 1.22 thorpej mod.handle = _nsbuiltin; 312 1.22 thorpej } else if (!is_dynamic()) { 313 1.22 thorpej goto out; 314 1.22 thorpej } else { 315 1.22 thorpej #ifdef __ELF__ 316 1.22 thorpej if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, 317 1.22 thorpej NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) 318 1.22 thorpej goto out; 319 1.22 thorpej mod.handle = dlopen(buf, RTLD_LOCAL | RTLD_LAZY); 320 1.22 thorpej if (mod.handle == NULL) { 321 1.22 thorpej #ifdef _NSS_DEBUG 322 1.22 thorpej /* 323 1.22 thorpej * This gets pretty annoying, since the built-in 324 1.22 thorpej * sources are not yet modules. 325 1.22 thorpej */ 326 1.22 thorpej /* XXX log some error? */ 327 1.22 thorpej #endif 328 1.22 thorpej goto out; 329 1.22 thorpej } 330 1.22 thorpej reg_fn = (nss_module_register_fn) dlsym(mod.handle, 331 1.22 thorpej "nss_module_register"); 332 1.22 thorpej if (reg_fn == NULL) { 333 1.22 thorpej (void) dlclose(mod.handle); 334 1.22 thorpej mod.handle = NULL; 335 1.22 thorpej /* XXX log some error? */ 336 1.22 thorpej goto out; 337 1.22 thorpej } 338 1.22 thorpej #else /* ! __ELF__ */ 339 1.22 thorpej mod.handle = NULL; 340 1.22 thorpej #endif /* __ELF__ */ 341 1.22 thorpej } 342 1.22 thorpej mod.mtab = (*reg_fn)(mod.name, &mod.mtabsize, &mod.unregister); 343 1.22 thorpej if (mod.mtab == NULL || mod.mtabsize == 0) { 344 1.22 thorpej #ifdef __ELF__ 345 1.22 thorpej if (mod.handle != _nsbuiltin) 346 1.22 thorpej (void) dlclose(mod.handle); 347 1.22 thorpej #endif /* __ELF__ */ 348 1.22 thorpej mod.handle = NULL; 349 1.22 thorpej /* XXX log some error? */ 350 1.22 thorpej goto out; 351 1.22 thorpej } 352 1.22 thorpej if (mod.mtabsize > 1) 353 1.22 thorpej qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), 354 1.22 thorpej _nsmtabcmp); 355 1.22 thorpej out: 356 1.22 thorpej new = _nsvect_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod)); 357 1.22 thorpej if (new == NULL) { 358 1.22 thorpej _nsmodfree(&mod); 359 1.22 thorpej return (-1); 360 1.22 thorpej } 361 1.22 thorpej _nsmod = new; 362 1.22 thorpej /* _nsmodsize already incremented */ 363 1.22 thorpej 364 1.22 thorpej qsort(_nsmod, _nsmodsize, sizeof(*_nsmod), _nsmodcmp); 365 1.22 thorpej return (0); 366 1.22 thorpej } 367 1.22 thorpej 368 1.22 thorpej static void 369 1.22 thorpej _nsloadbuiltin(void) 370 1.22 thorpej { 371 1.22 thorpej 372 1.22 thorpej /* Do nothing, for now. */ 373 1.22 thorpej } 374 1.2 lukem 375 1.15 lukem int 376 1.21 thorpej _nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) 377 1.2 lukem { 378 1.22 thorpej void *new; 379 1.22 thorpej const ns_mod *mod; 380 1.22 thorpej ns_mod modkey; 381 1.12 lukem 382 1.12 lukem _DIAGASSERT(dbt != NULL); 383 1.12 lukem _DIAGASSERT(src != NULL); 384 1.12 lukem 385 1.22 thorpej new = _nsvect_append(src, dbt->srclist, &dbt->srclistsize, 386 1.22 thorpej sizeof(*src)); 387 1.22 thorpej if (new == NULL) 388 1.22 thorpej return (-1); 389 1.22 thorpej dbt->srclist = new; 390 1.22 thorpej /* dbt->srclistsize already incremented */ 391 1.22 thorpej 392 1.22 thorpej modkey.name = src->name; 393 1.37 christos mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), _nsmodcmp); 394 1.22 thorpej if (mod == NULL) 395 1.22 thorpej return (_nsloadmod(src->name, NULL)); 396 1.15 lukem 397 1.15 lukem return (0); 398 1.2 lukem } 399 1.2 lukem 400 1.2 lukem void 401 1.21 thorpej _nsdbtdump(const ns_dbt *dbt) 402 1.2 lukem { 403 1.34 lukem unsigned int i; 404 1.2 lukem 405 1.12 lukem _DIAGASSERT(dbt != NULL); 406 1.12 lukem 407 1.2 lukem printf("%s (%d source%s):", dbt->name, dbt->srclistsize, 408 1.2 lukem dbt->srclistsize == 1 ? "" : "s"); 409 1.2 lukem for (i = 0; i < dbt->srclistsize; i++) { 410 1.2 lukem printf(" %s", dbt->srclist[i].name); 411 1.2 lukem if (!(dbt->srclist[i].flags & 412 1.2 lukem (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && 413 1.2 lukem (dbt->srclist[i].flags & NS_SUCCESS)) 414 1.2 lukem continue; 415 1.2 lukem printf(" ["); 416 1.2 lukem if (!(dbt->srclist[i].flags & NS_SUCCESS)) 417 1.2 lukem printf(" SUCCESS=continue"); 418 1.2 lukem if (dbt->srclist[i].flags & NS_UNAVAIL) 419 1.2 lukem printf(" UNAVAIL=return"); 420 1.2 lukem if (dbt->srclist[i].flags & NS_NOTFOUND) 421 1.2 lukem printf(" NOTFOUND=return"); 422 1.2 lukem if (dbt->srclist[i].flags & NS_TRYAGAIN) 423 1.2 lukem printf(" TRYAGAIN=return"); 424 1.2 lukem printf(" ]"); 425 1.2 lukem } 426 1.2 lukem printf("\n"); 427 1.2 lukem } 428 1.2 lukem 429 1.22 thorpej static void 430 1.22 thorpej _nssrclist_free(ns_src **src, u_int srclistsize) 431 1.2 lukem { 432 1.22 thorpej u_int i; 433 1.2 lukem 434 1.22 thorpej for (i = 0; i < srclistsize; i++) { 435 1.30 christos if ((*src)[i].name != NULL) 436 1.30 christos free(__UNCONST((*src)[i].name)); 437 1.22 thorpej } 438 1.22 thorpej free(*src); 439 1.22 thorpej *src = NULL; 440 1.22 thorpej } 441 1.2 lukem 442 1.22 thorpej static void 443 1.22 thorpej _nsdbtfree(ns_dbt *dbt) 444 1.22 thorpej { 445 1.2 lukem 446 1.22 thorpej _nssrclist_free(&dbt->srclist, dbt->srclistsize); 447 1.30 christos if (dbt->name != NULL) 448 1.30 christos free(__UNCONST(dbt->name)); 449 1.2 lukem } 450 1.2 lukem 451 1.15 lukem int 452 1.21 thorpej _nsdbtput(const ns_dbt *dbt) 453 1.2 lukem { 454 1.22 thorpej ns_dbt *p; 455 1.22 thorpej void *new; 456 1.22 thorpej u_int i; 457 1.2 lukem 458 1.12 lukem _DIAGASSERT(dbt != NULL); 459 1.12 lukem 460 1.2 lukem for (i = 0; i < _nsmapsize; i++) { 461 1.22 thorpej p = _nsvect_elem(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); 462 1.22 thorpej if (strcasecmp(dbt->name, p->name) == 0) { 463 1.2 lukem /* overwrite existing entry */ 464 1.22 thorpej if (p->srclist != NULL) 465 1.22 thorpej _nssrclist_free(&p->srclist, p->srclistsize); 466 1.22 thorpej memmove(p, dbt, sizeof(*dbt)); 467 1.15 lukem return (0); 468 1.2 lukem } 469 1.2 lukem } 470 1.22 thorpej new = _nsvect_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap)); 471 1.22 thorpej if (new == NULL) 472 1.22 thorpej return (-1); 473 1.22 thorpej _nsmap = new; 474 1.22 thorpej /* _nsmapsize already incremented */ 475 1.2 lukem 476 1.22 thorpej return (0); 477 1.22 thorpej } 478 1.22 thorpej 479 1.22 thorpej /* 480 1.22 thorpej * This function is called each time nsdispatch() is called. If this 481 1.22 thorpej * is the first call, or if the configuration has changed, (re-)prepare 482 1.22 thorpej * the global data used by NSS. 483 1.22 thorpej */ 484 1.22 thorpej static int 485 1.22 thorpej _nsconfigure(void) 486 1.22 thorpej { 487 1.22 thorpej #ifdef _REENTRANT 488 1.22 thorpej static mutex_t _nsconflock = MUTEX_INITIALIZER; 489 1.22 thorpej #endif 490 1.22 thorpej static time_t _nsconfmod; 491 1.22 thorpej struct stat statbuf; 492 1.15 lukem 493 1.22 thorpej mutex_lock(&_nsconflock); 494 1.22 thorpej 495 1.22 thorpej if (stat(_PATH_NS_CONF, &statbuf) == -1) { 496 1.22 thorpej /* 497 1.22 thorpej * No nsswitch.conf; just use whatever configuration we 498 1.22 thorpej * currently have, or fall back on the defaults specified 499 1.22 thorpej * by the caller. 500 1.22 thorpej */ 501 1.22 thorpej mutex_unlock(&_nsconflock); 502 1.22 thorpej return (0); 503 1.22 thorpej } 504 1.22 thorpej 505 1.22 thorpej if (statbuf.st_mtime <= _nsconfmod) { 506 1.22 thorpej /* Internal state is up-to-date with nsswitch.conf. */ 507 1.22 thorpej mutex_unlock(&_nsconflock); 508 1.22 thorpej return (0); 509 1.22 thorpej } 510 1.22 thorpej 511 1.22 thorpej /* 512 1.22 thorpej * Ok, we've decided we need to update the nsswitch configuration 513 1.23 thorpej * structures. Acquire a write-lock on _nslock while continuing 514 1.23 thorpej * to hold _nsconflock. Acquiring a write-lock blocks while 515 1.23 thorpej * waiting for other threads already holding a read-lock to clear. 516 1.23 thorpej * We hold _nsconflock for the duration, and update the time stamp 517 1.23 thorpej * at the end of the update operation, at which time we release 518 1.23 thorpej * both locks. 519 1.22 thorpej */ 520 1.22 thorpej rwlock_wrlock(&_nslock); 521 1.22 thorpej 522 1.38 christos _nsyyin = fopen(_PATH_NS_CONF, "re"); 523 1.22 thorpej if (_nsyyin == NULL) { 524 1.22 thorpej /* 525 1.22 thorpej * Unable to open nsswitch.conf; behave as though the 526 1.22 thorpej * stat() above failed. Even though we have already 527 1.22 thorpej * updated _nsconfmod, if the file reappears, the 528 1.22 thorpej * mtime will change. 529 1.22 thorpej */ 530 1.23 thorpej goto out; 531 1.2 lukem } 532 1.22 thorpej 533 1.22 thorpej _NSVECT_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), 534 1.22 thorpej (_nsvect_free_elem) _nsdbtfree); 535 1.22 thorpej _NSVECT_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), 536 1.22 thorpej (_nsvect_free_elem) _nsmodfree); 537 1.22 thorpej 538 1.22 thorpej _nsloadbuiltin(); 539 1.22 thorpej 540 1.22 thorpej _nsyyparse(); 541 1.22 thorpej (void) fclose(_nsyyin); 542 1.22 thorpej if (_nsmapsize != 0) 543 1.22 thorpej qsort(_nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 544 1.23 thorpej 545 1.23 thorpej _nsconfmod = statbuf.st_mtime; 546 1.23 thorpej 547 1.23 thorpej out: 548 1.22 thorpej rwlock_unlock(&_nslock); 549 1.23 thorpej mutex_unlock(&_nsconflock); 550 1.15 lukem return (0); 551 1.2 lukem } 552 1.2 lukem 553 1.22 thorpej static nss_method 554 1.22 thorpej _nsmethod(const char *source, const char *database, const char *method, 555 1.22 thorpej const ns_dtab disp_tab[], void **cb_data) 556 1.22 thorpej { 557 1.22 thorpej int curdisp; 558 1.22 thorpej ns_mod *mod, modkey; 559 1.22 thorpej ns_mtab *mtab, mtabkey; 560 1.22 thorpej 561 1.22 thorpej if (disp_tab != NULL) { 562 1.22 thorpej for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++) { 563 1.22 thorpej if (strcasecmp(source, disp_tab[curdisp].src) == 0) { 564 1.22 thorpej *cb_data = disp_tab[curdisp].cb_data; 565 1.22 thorpej return (disp_tab[curdisp].callback); 566 1.22 thorpej } 567 1.22 thorpej } 568 1.22 thorpej } 569 1.22 thorpej 570 1.22 thorpej modkey.name = source; 571 1.22 thorpej mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), 572 1.22 thorpej _nsmodcmp); 573 1.22 thorpej if (mod != NULL && mod->handle != NULL) { 574 1.22 thorpej mtabkey.database = database; 575 1.22 thorpej mtabkey.name = method; 576 1.22 thorpej mtab = bsearch(&mtabkey, mod->mtab, mod->mtabsize, 577 1.22 thorpej sizeof(mod->mtab[0]), _nsmtabcmp); 578 1.22 thorpej if (mtab != NULL) { 579 1.22 thorpej *cb_data = mtab->mdata; 580 1.22 thorpej return (mtab->method); 581 1.22 thorpej } 582 1.22 thorpej } 583 1.22 thorpej 584 1.22 thorpej *cb_data = NULL; 585 1.22 thorpej return (NULL); 586 1.22 thorpej } 587 1.2 lukem 588 1.2 lukem int 589 1.11 christos /*ARGSUSED*/ 590 1.5 lukem nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, 591 1.5 lukem const char *method, const ns_src defaults[], ...) 592 1.2 lukem { 593 1.23 thorpej static int _nsdispatching; 594 1.23 thorpej #ifdef _REENTRANT 595 1.23 thorpej struct _ns_drec drec, *ldrec; 596 1.23 thorpej #endif 597 1.2 lukem va_list ap; 598 1.22 thorpej int i, result; 599 1.22 thorpej ns_dbt key; 600 1.2 lukem const ns_dbt *dbt; 601 1.5 lukem const ns_src *srclist; 602 1.5 lukem int srclistsize; 603 1.22 thorpej nss_method cb; 604 1.22 thorpej void *cb_data; 605 1.12 lukem 606 1.25 lukem /* retval may be NULL */ 607 1.25 lukem /* disp_tab may be NULL */ 608 1.12 lukem _DIAGASSERT(database != NULL); 609 1.12 lukem _DIAGASSERT(method != NULL); 610 1.25 lukem _DIAGASSERT(defaults != NULL); 611 1.25 lukem if (database == NULL || method == NULL || defaults == NULL) 612 1.12 lukem return (NS_UNAVAIL); 613 1.2 lukem 614 1.23 thorpej /* 615 1.23 thorpej * In both the threaded and non-threaded cases, avoid reloading 616 1.23 thorpej * the configuration if the current thread is already running 617 1.23 thorpej * nsdispatch() (i.e. recursive call). 618 1.23 thorpej * 619 1.23 thorpej * In the non-threaded case, this avoids changing the data structures 620 1.23 thorpej * while we're using them. 621 1.23 thorpej * 622 1.23 thorpej * In the threaded case, this avoids trying to take a write lock 623 1.23 thorpej * while the current thread holds a read lock (which would result 624 1.23 thorpej * in deadlock). 625 1.23 thorpej */ 626 1.23 thorpej #ifdef _REENTRANT 627 1.23 thorpej if (__isthreaded) { 628 1.23 thorpej drec.thr = thr_self(); 629 1.23 thorpej mutex_lock(&_ns_drec_lock); 630 1.23 thorpej LIST_FOREACH(ldrec, &_ns_drec, list) { 631 1.23 thorpej if (ldrec->thr == drec.thr) 632 1.23 thorpej break; 633 1.23 thorpej } 634 1.23 thorpej LIST_INSERT_HEAD(&_ns_drec, &drec, list); 635 1.23 thorpej mutex_unlock(&_ns_drec_lock); 636 1.23 thorpej if (ldrec == NULL && _nsconfigure()) { 637 1.23 thorpej mutex_lock(&_ns_drec_lock); 638 1.23 thorpej LIST_REMOVE(&drec, list); 639 1.23 thorpej mutex_unlock(&_ns_drec_lock); 640 1.23 thorpej return (NS_UNAVAIL); 641 1.23 thorpej } 642 1.29 thorpej } else 643 1.29 thorpej #endif /* _REENTRANT */ 644 1.29 thorpej if (_nsdispatching++ == 0 && _nsconfigure()) { 645 1.29 thorpej _nsdispatching--; 646 1.29 thorpej return (NS_UNAVAIL); 647 1.23 thorpej } 648 1.22 thorpej 649 1.22 thorpej rwlock_rdlock(&_nslock); 650 1.22 thorpej 651 1.22 thorpej key.name = database; 652 1.22 thorpej dbt = bsearch(&key, _nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 653 1.5 lukem if (dbt != NULL) { 654 1.5 lukem srclist = dbt->srclist; 655 1.5 lukem srclistsize = dbt->srclistsize; 656 1.5 lukem } else { 657 1.5 lukem srclist = defaults; 658 1.5 lukem srclistsize = 0; 659 1.5 lukem while (srclist[srclistsize].name != NULL) 660 1.5 lukem srclistsize++; 661 1.5 lukem } 662 1.2 lukem result = 0; 663 1.2 lukem 664 1.5 lukem for (i = 0; i < srclistsize; i++) { 665 1.22 thorpej cb = _nsmethod(srclist[i].name, database, method, 666 1.22 thorpej disp_tab, &cb_data); 667 1.2 lukem result = 0; 668 1.22 thorpej if (cb != NULL) { 669 1.5 lukem va_start(ap, defaults); 670 1.22 thorpej result = (*cb)(retval, cb_data, ap); 671 1.2 lukem va_end(ap); 672 1.26 lukem if (defaults[0].flags & NS_FORCEALL) 673 1.26 lukem continue; 674 1.22 thorpej if (result & srclist[i].flags) 675 1.2 lukem break; 676 1.2 lukem } 677 1.2 lukem } 678 1.27 lukem result &= NS_STATUSMASK; /* clear private flags in result */ 679 1.22 thorpej 680 1.22 thorpej rwlock_unlock(&_nslock); 681 1.22 thorpej 682 1.23 thorpej #ifdef _REENTRANT 683 1.23 thorpej if (__isthreaded) { 684 1.23 thorpej mutex_lock(&_ns_drec_lock); 685 1.23 thorpej LIST_REMOVE(&drec, list); 686 1.23 thorpej mutex_unlock(&_ns_drec_lock); 687 1.23 thorpej } else 688 1.23 thorpej #endif /* _REENTRANT */ 689 1.29 thorpej _nsdispatching--; 690 1.23 thorpej 691 1.2 lukem return (result ? result : NS_NOTFOUND); 692 1.2 lukem } 693