diff options
author | nectar <nectar@FreeBSD.org> | 2003-04-17 14:14:22 +0000 |
---|---|---|
committer | nectar <nectar@FreeBSD.org> | 2003-04-17 14:14:22 +0000 |
commit | 1b1f6bb4f50d42bbbb1291be0c60741c12f8201a (patch) | |
tree | 72f30804c92fca1b7ff1088806b0e3f696dce64c /lib/libc/net/nsdispatch.c | |
parent | 7ec422366401f961c808e40fd6b4c95c955ea58f (diff) | |
download | FreeBSD-src-1b1f6bb4f50d42bbbb1291be0c60741c12f8201a.zip FreeBSD-src-1b1f6bb4f50d42bbbb1291be0c60741c12f8201a.tar.gz |
= Implement name service switch modules (NSS modules). NSS modules
may be built into libc (`static NSS modules') or dynamically loaded
via dlopen (`dynamic NSS modules'). Modules are loaded/initialized
at configuration time (i.e. when nsdispatch is called and nsswitch.conf
is read or re-read).
= Make the nsdispatch(3) core thread-safe.
= New status code for nsdispatch(3) `NS_RETURN', currently used to
signal ERANGE-type issues.
= syslog(3) problems, don't warn/err/abort.
= Try harder to avoid namespace pollution.
= Implement some shims to assist in porting NSS modules written for
the GNU C Library nsswitch interface.
Sponsored by: DARPA, Network Associates Laboratories
Diffstat (limited to 'lib/libc/net/nsdispatch.c')
-rw-r--r-- | lib/libc/net/nsdispatch.c | 584 |
1 files changed, 469 insertions, 115 deletions
diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c index b7c59ff..60a3eba 100644 --- a/lib/libc/net/nsdispatch.c +++ b/lib/libc/net/nsdispatch.c @@ -35,25 +35,75 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ - +/*- + * Copyright (c) 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * Portions of this software were developed for the FreeBSD Project by + * Jacques A. Vidrine, Safeport Network Services, and Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <sys/types.h> +#include "namespace.h" #include <sys/param.h> #include <sys/stat.h> -#include "namespace.h" -#include <err.h> +#include <dlfcn.h> +#include <errno.h> #include <fcntl.h> #define _NS_PRIVATE #include <nsswitch.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <syslog.h> #include <unistd.h> #include "un-namespace.h" +enum _nss_constants { + /* Number of elements allocated when we grow a vector */ + ELEMSPERCHUNK = 8 +}; + +/* + * Global NSS data structures are mostly read-only, but we update + * them when we read or re-read the nsswitch.conf. + */ +static pthread_rwlock_t nss_lock = PTHREAD_RWLOCK_INITIALIZER; + +/* + * Runtime determination of whether we are dynamically linked or not. + */ +extern int _DYNAMIC __attribute__ ((weak)); +#define is_dynamic() (&_DYNAMIC != NULL) + /* * default sourcelist: `files' */ @@ -62,53 +112,178 @@ const ns_src __nsdefaultsrc[] = { { 0 }, }; - -static int _nsmapsize = 0; +/* Database, source mappings. */ +static unsigned int _nsmapsize; static ns_dbt *_nsmap = NULL; +/* NSS modules. */ +static unsigned int _nsmodsize; +static ns_mod *_nsmod; + +/* Placeholder for builtin modules' dlopen `handle'. */ +static int __nss_builtin_handle; +static void *nss_builtin_handle = &__nss_builtin_handle; + /* - * size of dynamic array chunk for _nsmap and _nsmap[x].srclist + * Attempt to spew relatively uniform messages to syslog. */ -#define NSELEMSPERCHUNK 8 +#define nss_log(level, fmt, ...) \ + syslog((level), "NSSWITCH(%s): " fmt, __func__, __VA_ARGS__) +#define nss_log_simple(level, s) \ + syslog((level), "NSSWITCH(%s): " s, __func__) +/* + * Dynamically growable arrays are used for lists of databases, sources, + * and modules. The following `vector' interface is used to isolate the + * common operations. + */ +typedef int (*vector_comparison)(const void *, const void *); +typedef void (*vector_free_elem)(void *); +static void vector_sort(void *, unsigned int, size_t, + vector_comparison); +static void vector_free(void **, unsigned int *, size_t, + vector_free_elem); +static void *vector_ref(unsigned int, void *, unsigned int, size_t); +static void *vector_search(const void *, void *, unsigned int, size_t, + vector_comparison); +static int vector_append(const void *, void **, unsigned int *, size_t); -int _nscmp(const void *, const void *); +/* + * Internal interfaces. + */ +static int string_compare(const void *, const void *); +static int mtab_compare(const void *, const void *); +static int nss_configure(void); +static void ns_dbt_free(ns_dbt *); +static void ns_mod_free(ns_mod *); +static void ns_src_free(ns_src **, int); +static void nss_load_builtin_modules(void); +static void nss_load_module(const char *, nss_module_register_fn); +static void nss_atexit(void); +/* nsparser */ +extern FILE *_nsyyin; -int -_nscmp(a, b) - const void *a; - const void *b; + +/* + * The vector operations + */ +static void +vector_sort(void *vec, unsigned int count, size_t esize, + vector_comparison comparison) { - return (strcasecmp(((const ns_dbt *)a)->name, - ((const ns_dbt *)b)->name)); + qsort(vec, count, esize, comparison); } -void -_nsdbtaddsrc(dbt, src) - ns_dbt *dbt; - const ns_src *src; -{ - if ((dbt->srclistsize % NSELEMSPERCHUNK) == 0) { - dbt->srclist = (ns_src *)realloc(dbt->srclist, - (dbt->srclistsize + NSELEMSPERCHUNK) * sizeof(ns_src)); - if (dbt->srclist == NULL) - _err(1, "nsdispatch: memory allocation failure"); +static void * +vector_search(const void *key, void *vec, unsigned int count, size_t esize, + vector_comparison comparison) +{ + return (bsearch(key, vec, count, esize, comparison)); +} + + +static int +vector_append(const void *elem, void **vec, unsigned int *count, size_t esize) +{ + void *p; + + if ((*count % ELEMSPERCHUNK) == 0) { + p = realloc(*vec, (*count + ELEMSPERCHUNK) * esize); + if (p == NULL) { + nss_log_simple(LOG_ERR, "memory allocation failure"); + return (0); + } else + *vec = p; } - memmove(&dbt->srclist[dbt->srclistsize++], src, sizeof(ns_src)); + memmove((void *)(((uintptr_t)*vec) + (*count * esize)), + elem, esize); + (*count)++; + return (1); +} + + +static void * +vector_ref(unsigned int i, void *vec, unsigned int count, size_t esize) +{ + if (i < count) + return (void *)((uintptr_t)vec + (i * esize)); + else + return (NULL); +} + + +static void +vector_free(void **vec, unsigned int *count, size_t esize, + vector_free_elem free_elem) +{ + unsigned int i; + void *elem; + + for (i = 0; i < *count; i++) { + elem = vector_ref(i, *vec, *count, esize); + if (elem != NULL) + free_elem(elem); + } + free(*vec); + *vec = NULL; + *count = 0; +} + + + +/* + * Comparison functions for vector_search. + */ +static int +string_compare(const void *a, const void *b) +{ + return (strcasecmp(*(const char * const *)a, *(const char * const *)b)); +} + + +static int +mtab_compare(const void *a, const void *b) +{ + int cmp; + + cmp = strcmp(((const ns_mtab *)a)->name, ((const ns_mtab *)b)->name); + if (cmp != 0) + return (cmp); + else + return (strcmp(((const ns_mtab *)a)->database, + ((const ns_mtab *)b)->database)); +} + + + +/* + * NSS nsmap management. + */ +void +_nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) +{ + const ns_mod *modp; + + vector_append(src, (void **)&dbt->srclist, &dbt->srclistsize, + sizeof(*src)); + modp = vector_search(&src->name, _nsmod, _nsmodsize, sizeof(*_nsmod), + string_compare); + if (modp == NULL) + nss_load_module(src->name, NULL); } +#ifdef _NSS_DEBUG void -_nsdbtdump(dbt) - const ns_dbt *dbt; +_nsdbtdump(const ns_dbt *dbt) { int i; printf("%s (%d source%s):", dbt->name, dbt->srclistsize, dbt->srclistsize == 1 ? "" : "s"); - for (i = 0; i < dbt->srclistsize; i++) { + for (i = 0; i < (int)dbt->srclistsize; i++) { printf(" %s", dbt->srclist[i].name); if (!(dbt->srclist[i].flags & (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && @@ -127,103 +302,285 @@ _nsdbtdump(dbt) } printf("\n"); } +#endif -const ns_dbt * -_nsdbtget(name) - const char *name; +/* + * The first time nsdispatch is called (during a process's lifetime, + * or after nsswitch.conf has been updated), nss_configure will + * prepare global data needed by NSS. + */ +static int +nss_configure(void) { - static time_t confmod; - + static pthread_mutex_t conf_lock = PTHREAD_MUTEX_INITIALIZER; + static time_t confmod; struct stat statbuf; - ns_dbt dbt; - - extern FILE *_nsyyin; - extern int _nsyyparse(void); - - dbt.name = name; - - if (confmod) { - if (stat(_PATH_NS_CONF, &statbuf) == -1) - return (NULL); - if (confmod < statbuf.st_mtime) { - int i, j; - - for (i = 0; i < _nsmapsize; i++) { - for (j = 0; j < _nsmap[i].srclistsize; j++) { - if (_nsmap[i].srclist[j].name != NULL) { - /*LINTED const cast*/ - free((void *) - _nsmap[i].srclist[j].name); - } - } - if (_nsmap[i].srclist) - free(_nsmap[i].srclist); - if (_nsmap[i].name) { - /*LINTED const cast*/ - free((void *)_nsmap[i].name); - } - } - if (_nsmap) - free(_nsmap); - _nsmap = NULL; - _nsmapsize = 0; - confmod = 0; - } - } - if (!confmod) { - if (stat(_PATH_NS_CONF, &statbuf) == -1) - return (NULL); - _nsyyin = fopen(_PATH_NS_CONF, "r"); - if (_nsyyin == NULL) - return (NULL); - _nsyyparse(); - (void)fclose(_nsyyin); - qsort(_nsmap, (size_t)_nsmapsize, sizeof(ns_dbt), _nscmp); - confmod = statbuf.st_mtime; - } - return (bsearch(&dbt, _nsmap, (size_t)_nsmapsize, sizeof(ns_dbt), - _nscmp)); + int result; + const char *path; + +#if defined(_NSS_DEBUG) && defined(_NSS_SHOOT_FOOT) + /* NOTE WELL: THIS IS A SECURITY HOLE. This must only be built + * for debugging purposes and MUST NEVER be used in production. + */ + path = getenv("NSSWITCH_CONF"); + if (path == NULL) +#endif + path = _PATH_NS_CONF; + if (stat(path, &statbuf) != 0) + return (0); + if (statbuf.st_mtime <= confmod) + return (0); + result = _pthread_mutex_trylock(&conf_lock); + if (result != 0) + return (0); + (void)_pthread_rwlock_unlock(&nss_lock); + result = _pthread_rwlock_wrlock(&nss_lock); + if (result != 0) + goto fin2; + _nsyyin = fopen(path, "r"); + if (_nsyyin == NULL) + goto fin; + vector_free((void **)&_nsmap, &_nsmapsize, sizeof(*_nsmap), + (vector_free_elem)ns_dbt_free); + vector_free((void **)&_nsmod, &_nsmodsize, sizeof(*_nsmod), + (vector_free_elem)ns_mod_free); + nss_load_builtin_modules(); + _nsyyparse(); + (void)fclose(_nsyyin); + vector_sort(_nsmap, _nsmapsize, sizeof(*_nsmap), string_compare); + if (confmod == 0) + (void)atexit(nss_atexit); + confmod = statbuf.st_mtime; +fin: + (void)_pthread_rwlock_unlock(&nss_lock); + result = _pthread_rwlock_rdlock(&nss_lock); +fin2: + (void)_pthread_mutex_unlock(&conf_lock); + return (result); } void -_nsdbtput(dbt) - const ns_dbt *dbt; +_nsdbtput(const ns_dbt *dbt) { - int i; + unsigned int i; + ns_dbt *p; for (i = 0; i < _nsmapsize; i++) { - if (_nscmp(dbt, &_nsmap[i]) == 0) { - /* overwrite existing entry */ - if (_nsmap[i].srclist != NULL) - free(_nsmap[i].srclist); - memmove(&_nsmap[i], dbt, sizeof(ns_dbt)); + p = vector_ref(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); + if (string_compare(&dbt->name, &p->name) == 0) { + /* overwrite existing entry */ + if (p->srclist != NULL) + ns_src_free(&p->srclist, p->srclistsize); + memmove(p, dbt, sizeof(*dbt)); return; } } + vector_append(dbt, (void **)&_nsmap, &_nsmapsize, sizeof(*_nsmap)); +} + + +static void +ns_dbt_free(ns_dbt *dbt) +{ + ns_src_free(&dbt->srclist, dbt->srclistsize); +} - if ((_nsmapsize % NSELEMSPERCHUNK) == 0) { - _nsmap = (ns_dbt *)realloc(_nsmap, - (_nsmapsize + NSELEMSPERCHUNK) * sizeof(ns_dbt)); - if (_nsmap == NULL) - _err(1, "nsdispatch: memory allocation failure"); + +static void +ns_src_free(ns_src **src, int srclistsize) +{ + int i; + + for (i = 0; i < srclistsize; i++) + if ((*src)[i].name != NULL) + /* This one was allocated by nslexer. You'll just + * have to trust me. + */ + free((void *)((*src)[i].name)); + free(*src); + *src = NULL; +} + + + +/* + * NSS module management. + */ +/* The built-in NSS modules are all loaded at once. */ +#define NSS_BACKEND(name, reg) \ +ns_mtab *reg(unsigned int *, nss_module_unregister_fn *); +#include "nss_backends.h" +#undef NSS_BACKEND + +static void +nss_load_builtin_modules(void) +{ +#define NSS_BACKEND(name, reg) nss_load_module(#name, reg); +#include "nss_backends.h" +#undef NSS_BACKEND +} + + +/* Load a built-in or dynamically linked module. If the `reg_fn' + * argument is non-NULL, assume a built-in module and use reg_fn to + * register it. Otherwise, search for a dynamic NSS module. + */ +static void +nss_load_module(const char *source, nss_module_register_fn reg_fn) +{ + char buf[PATH_MAX]; + ns_mod mod; + nss_module_register_fn fn; + + memset(&mod, 0, sizeof(mod)); + mod.name = strdup(source); + if (mod.name == NULL) { + nss_log_simple(LOG_ERR, "memory allocation failure"); + return; } - memmove(&_nsmap[_nsmapsize++], dbt, sizeof(ns_dbt)); + if (reg_fn != NULL) { + /* The placeholder is required, as a NULL handle + * represents an invalid module. + */ + mod.handle = nss_builtin_handle; + fn = reg_fn; + } else if (!is_dynamic()) + goto fin; + else { + if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, + NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) + goto fin; + mod.handle = dlopen(buf, RTLD_LOCAL|RTLD_LAZY); + if (mod.handle == NULL) { +#ifdef _NSS_DEBUG + /* This gets pretty annoying since the built-in + * sources aren't modules yet. + */ + nss_log(LOG_DEBUG, "%s, %s", mod.name, dlerror()); +#endif + goto fin; + } + fn = (nss_module_register_fn)dlfunc(mod.handle, + "nss_module_register"); + if (fn == NULL) { + (void)dlclose(mod.handle); + mod.handle = NULL; + nss_log(LOG_ERR, "%s, %s", mod.name, dlerror()); + goto fin; + } + } + mod.mtab = fn(mod.name, &mod.mtabsize, &mod.unregister); + if (mod.mtab == NULL || mod.mtabsize == 0) { + if (mod.handle != nss_builtin_handle) + (void)dlclose(mod.handle); + mod.handle = NULL; + nss_log(LOG_ERR, "%s, registration failed", mod.name); + goto fin; + } + if (mod.mtabsize > 1) + qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), + mtab_compare); +fin: + vector_append(&mod, (void **)&_nsmod, &_nsmodsize, sizeof(*_nsmod)); + vector_sort(_nsmod, _nsmodsize, sizeof(*_nsmod), string_compare); +} + + + +static void +ns_mod_free(ns_mod *mod) +{ + + free(mod->name); + if (mod->handle == NULL) + return; + if (mod->unregister != NULL) + mod->unregister(mod->mtab, mod->mtabsize); + if (mod->handle != nss_builtin_handle) + (void)dlclose(mod->handle); } + +/* + * Cleanup + */ +static void +nss_atexit(void) +{ + (void)_pthread_rwlock_wrlock(&nss_lock); + vector_free((void **)&_nsmap, &_nsmapsize, sizeof(*_nsmap), + (vector_free_elem)ns_dbt_free); + vector_free((void **)&_nsmod, &_nsmodsize, sizeof(*_nsmod), + (vector_free_elem)ns_mod_free); + (void)_pthread_rwlock_unlock(&nss_lock); +} + + + +/* + * Finally, the actual implementation. + */ +static nss_method +nss_method_lookup(const char *source, const char *database, + const char *method, const ns_dtab disp_tab[], void **mdata) +{ + ns_mod *mod; + ns_mtab *match, key; + int i; + + if (disp_tab != NULL) + for (i = 0; disp_tab[i].src != NULL; i++) + if (strcasecmp(source, disp_tab[i].src) == 0) { + *mdata = disp_tab[i].mdata; + return (disp_tab[i].method); + } + mod = vector_search(&source, _nsmod, _nsmodsize, sizeof(*_nsmod), + string_compare); + if (mod != NULL && mod->handle != NULL) { + key.database = database; + key.name = method; + match = bsearch(&key, mod->mtab, mod->mtabsize, + sizeof(mod->mtab[0]), mtab_compare); + if (match != NULL) { + *mdata = match->mdata; + return (match->method); + } + } + nss_log(LOG_DEBUG, "%s, %s, %s, not found", source, database, method); + *mdata = NULL; + return (NULL); +} + + +__weak_reference(_nsdispatch, nsdispatch); + int -nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, - const char *method, const ns_src defaults[], ...) +_nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, + const char *method_name, const ns_src defaults[], ...) { va_list ap; - int i, curdisp, result; const ns_dbt *dbt; const ns_src *srclist; - int srclistsize; - - dbt = _nsdbtget(database); + nss_method method; + void *mdata; + int serrno, i, result, srclistsize; + + serrno = errno; + result = _pthread_rwlock_rdlock(&nss_lock); + if (result != 0) { + result = NS_UNAVAIL; + goto fin; + } + result = nss_configure(); + if (result != 0) { + result = NS_UNAVAIL; + goto fin; + } + dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap), + string_compare); if (dbt != NULL) { srclist = dbt->srclist; srclistsize = dbt->srclistsize; @@ -233,23 +590,20 @@ nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, while (srclist[srclistsize].name != NULL) srclistsize++; } - result = 0; - for (i = 0; i < srclistsize; i++) { - for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++) - if (strcasecmp(disp_tab[curdisp].src, - srclist[i].name) == 0) - break; - result = 0; - if (disp_tab[curdisp].callback) { + result = NS_NOTFOUND; + method = nss_method_lookup(srclist[i].name, database, + method_name, disp_tab, &mdata); + if (method != NULL) { va_start(ap, defaults); - result = disp_tab[curdisp].callback(retval, - disp_tab[curdisp].cb_data, ap); + result = method(retval, mdata, ap); va_end(ap); - if (result & srclist[i].flags) { + if (result & (srclist[i].flags)) break; - } } } - return (result ? result : NS_NOTFOUND); + (void)_pthread_rwlock_unlock(&nss_lock); +fin: + errno = serrno; + return (result); } |