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 | |
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')
-rw-r--r-- | lib/libc/net/Makefile.inc | 2 | ||||
-rw-r--r-- | lib/libc/net/nsdispatch.3 | 147 | ||||
-rw-r--r-- | lib/libc/net/nsdispatch.c | 584 | ||||
-rw-r--r-- | lib/libc/net/nslexer.l | 14 | ||||
-rw-r--r-- | lib/libc/net/nsparser.y | 29 | ||||
-rw-r--r-- | lib/libc/net/nss_backends.h | 43 | ||||
-rw-r--r-- | lib/libc/net/nss_compat.c | 270 |
7 files changed, 885 insertions, 204 deletions
diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index ab9cb36..01dc3b61 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -15,7 +15,7 @@ SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c getaddrinfo.c \ inet_netof.c inet_network.c inet_ntoa.c inet_ntop.c \ inet_pton.c ip6opt.c linkaddr.c map_v4v6.c name6.c \ ns_name.c ns_netint.c ns_parse.c ns_print.c ns_ttl.c \ - nsdispatch.c nslexer.c nsparser.c \ + nsdispatch.c nslexer.c nsparser.c nss_compat.c \ nsap_addr.c rcmd.c rcmdsh.c recv.c res_comp.c res_data.c res_debug.c \ res_init.c res_mkquery.c res_mkupdate.c res_query.c res_send.c \ res_update.c rthdr.c send.c sockatmark.c vars.c diff --git a/lib/libc/net/nsdispatch.3 b/lib/libc/net/nsdispatch.3 index b87d122..d49a9d5 100644 --- a/lib/libc/net/nsdispatch.3 +++ b/lib/libc/net/nsdispatch.3 @@ -35,7 +35,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd January 19, 1999 +.Dd April 16, 2003 .Dt NSDISPATCH 3 .Os .Sh NAME @@ -44,98 +44,111 @@ .Sh LIBRARY .Lb libc .Sh SYNOPSIS +.In sys/types.h +.In stdarg.h .In nsswitch.h .Ft int .Fo nsdispatch .Fa "void *retval" .Fa "const ns_dtab dtab[]" .Fa "const char *database" -.Fa "const char *method" +.Fa "const char *method_name" .Fa "const ns_src defaults[]" .Fa "..." .Fc .Sh DESCRIPTION The .Fn nsdispatch -function invokes the callback functions specified in +function invokes the methods specified in .Va dtab -in the order given in -.Pa /etc/nsswitch.conf +in the order given by +.Xr nsswitch.conf 5 for the database .Va database until a successful entry is found. .Pp .Va retval -is passed to each callback function to modify as necessary -(to pass back to the caller of -.Fn nsdispatch ) +is passed to each method to modify as necessary, to pass back results to +the caller of +.Fn nsdispatch . +.Pp +Each method has the function signature described by the typedef: +.Pp +.Ft typedef "int \*(lp*nss_method\*(rp\*(lpvoid *retval, void *mdata, va_list *ap\*(rp" ; .Pp .Va dtab is an array of .Va ns_dtab structures, which have the following format: .Bd -literal -offset indent -typedef struct { - const char *src; - int (*cb)(void *retval, void *cb_data, va_list ap); - void *cb_data; +typedef struct _ns_dtab { + const char *src; + nss_method method; + void *mdata; } ns_dtab; .Ed .Pp .Bd -ragged -offset indent -For each source type that is implemented, an entry with +The +.Fa dtab +array should consist of one entry for each source type that is +implemented, with .Va src -set to the name of the source, -.Va cb -defined as a function which handles that source, and -.Va cb_data -is used to pass arbitrary data to the callback function. +as the name of the source, +.Va method +as a function which handles that source, and +.Va mdata +as a handle on arbitrary data to be passed to the method. The last entry in .Va dtab should contain .Dv NULL values for .Va src , -.Va cb , +.Va method , and -.Va cb_data . +.Va mdata . .Ed .Pp -.Va method -is usually the name of the function calling -.Fn nsdispatch . -When dynamic loading is supported, a symbol constructed from -.Va database , -the current source, and -.Va method -will be used as the name to invoke the dynamically loaded function. +Additionally, methods may be implemented in NSS modules, in +which case they are selected using the +.Fa database +and +.Fa method_name +arguments along with the configured source. +(The methods supplied via +.Fa dtab +take priority over those implemented in NSS modules in the event +of a conflict.) .Pp .Va defaults -contains a list of default sources to try in the case of -a missing or corrupt -.Xr nsswitch.conf 5 , -or if there isn't a relevant entry for +contains a list of default sources to try if +.Xr nsswitch.conf 5 +is missing or corrupted, or if there is no relevant entry for .Va database . It is an array of .Va ns_src structures, which have the following format: .Bd -literal -offset indent -typedef struct { - const char *src; - u_int32_t flags; +typedef struct _ns_src { + const char *src; + u_int32_t flags; } ns_src; .Ed .Pp .Bd -ragged -offset indent -For each default source type, an entry with -.Va src -set to the name of the source, and +The +.Fa defaults +array should consist of one entry for each source to be configured by +default indicated by +.Va src , +and .Va flags -set to the relevant flags +set to the criterion desired (usually .Dv NS_SUCCESS ; refer to -.Sx Callback return values +.Sx Method return values for more information). The last entry in .Va defaults @@ -149,18 +162,18 @@ set to 0. .Pp For convenience, a global variable defined as: .Dl extern const ns_src __nsdefaultsrc[]; -exists which contains a single default entry for +exists which contains a single default entry for the source .Sq files -for use by callers which don't require complicated default rules. +that may be used by callers which do not require complicated default +rules. .Ed .Pp .Sq Va ... -are optional extra arguments, which -are passed to the appropriate callback function as a variable argument -list of the type -.Va va_list . +are optional extra arguments, which are passed to the appropriate method +as a variable argument list of the type +.Vt va_list . .Ss Valid source types -Whilst there is support for arbitrary sources, the following +While there is support for arbitrary sources, the following #defines for commonly implemented sources are available: .Bl -column NS_COMPAT COMPAT -offset indent .It Sy "#define value" @@ -174,29 +187,32 @@ Refer to .Xr nsswitch.conf 5 for a complete description of what each source type is. .Pp -.Ss Callback return values -The callback functions should return one of the following values -depending upon status of the lookup: -.Bl -column NS_NOTFOUND -offset indent +.Ss Method return values +The +.Vt nss_method +functions must return one of the following values depending upon status +of the lookup: +.Bl -column "RETURN VALUE" "STATUS CODE" .It Sy "Return value Status code" .It "NS_SUCCESS success" .It "NS_NOTFOUND notfound" .It "NS_UNAVAIL unavail" .It "NS_TRYAGAIN tryagain" +.It "NS_RETURN -none-" .El .Pp Refer to .Xr nsswitch.conf 5 -for a complete description of what each status code is. +for a complete description of each status code. .Pp The .Fn nsdispatch -function returns the value of the callback that caused the dispatcher to -finish, or NS_NOTFOUND otherwise. +function returns the value of the method that caused the dispatcher to +terminate, or NS_NOTFOUND otherwise. .Sh SEE ALSO .Xr hesiod 3 , .Xr stdarg 3 , -.Xr ypclnt 3 , +.Xr yp 8 , .Xr nsswitch.conf 5 .Sh HISTORY The @@ -208,25 +224,22 @@ It was imported from the Project, where it appeared first in .Nx 1.4 . +Support for NSS modules first appeared in +.Fx 5.1 . .Sh AUTHORS Luke Mewburn .Aq lukem@netbsd.org -wrote this freely distributable name-service switch implementation, +wrote this freely-distributable name-service switch implementation, using ideas from the .Tn ULTRIX -.Xr svc.conf 5 +"svc.conf\*(lp5\*(rp" and .Tn Solaris -.Xr nsswitch.conf 4 +"nsswitch.conf\*(lp4\*(rp" manual pages. -.Sh BUGS The +.Fx Project +added the support for threads and NSS modules, and normalized the uses +of .Fn nsdispatch -function is not thread safe. -This will be rectified in the future. -.Pp -Currently there is no support for dynamically loadable dispatcher callback -functions. -It is anticipated that this will be added in the future in the back-end -without requiring changes to code that invokes -.Fn nsdispatch . +within the standard C library. 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); } diff --git a/lib/libc/net/nslexer.l b/lib/libc/net/nslexer.l index ea21072..34c79d9 100644 --- a/lib/libc/net/nslexer.l +++ b/lib/libc/net/nslexer.l @@ -45,10 +45,10 @@ static char *rcsid = #include "namespace.h" #include <ctype.h> -#include <err.h> #define _NS_PRIVATE #include <nsswitch.h> #include <string.h> +#include <syslog.h> #include "un-namespace.h" #include "nsparser.h" @@ -85,10 +85,11 @@ STRING [a-zA-Z][a-zA-Z0-9_]* char *p; int i; - if ((p = strdup(yytext)) == NULL) - _err(1, - "nsdispatch: memory allocation failure"); - + if ((p = strdup(yytext)) == NULL) { + syslog(LOG_ERR, + "NSSWITCH(nslexer): memory allocation failure"); + return ERRORTOKEN; + } for (i = 0; i < strlen(p); i++) { if (isupper((unsigned char)p[i])) p[i] = tolower((unsigned char)p[i]); @@ -113,5 +114,6 @@ _nsyyerror(msg) const char *msg; { - warnx("%s line %d: %s at '%s'", _PATH_NS_CONF, yylineno, msg, yytext); + syslog(LOG_ERR, "NSSWITCH(nslexer): %s line %d: %s at '%s'", + _PATH_NS_CONF, yylineno, msg, yytext); } /* _nsyyerror */ diff --git a/lib/libc/net/nsparser.y b/lib/libc/net/nsparser.y index dc3be2a..4806dea 100644 --- a/lib/libc/net/nsparser.y +++ b/lib/libc/net/nsparser.y @@ -38,17 +38,15 @@ */ #include <sys/cdefs.h> -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = - "$FreeBSD$"; -#endif /* LIBC_SCCS and not lint */ +__FBSDID("$FreeBSD$"); -#include <err.h> +#include "namespace.h" #define _NS_PRIVATE #include <nsswitch.h> #include <stdio.h> #include <string.h> - +#include <syslog.h> +#include "un-namespace.h" static void _nsaddsrctomap(const char *); @@ -64,6 +62,7 @@ static ns_src cursrc; %token NL %token SUCCESS UNAVAIL NOTFOUND TRYAGAIN %token RETURN CONTINUE +%token ERRORTOKEN %token <str> STRING %type <mapval> Status Action @@ -110,7 +109,7 @@ Srclist Item : STRING { - cursrc.flags = NS_SUCCESS; + cursrc.flags = NS_TERMINATE; _nsaddsrctomap($1); } | STRING '[' { cursrc.flags = NS_SUCCESS; } Criteria ']' @@ -127,9 +126,9 @@ Criteria Criterion : Status '=' Action { - if ($3) /* if action == RETURN set RETURN bit */ + if ($3) /* if action == RETURN set RETURN bit */ cursrc.flags |= $1; - else /* else unset it */ + else /* else unset it */ cursrc.flags &= ~$1; } ; @@ -142,8 +141,8 @@ Status ; Action - : RETURN { $$ = 1L; } - | CONTINUE { $$ = 0L; } + : RETURN { $$ = NS_ACTION_RETURN; } + | CONTINUE { $$ = NS_ACTION_CONTINUE; } ; %% @@ -160,16 +159,16 @@ _nsaddsrctomap(elem) if (curdbt.srclistsize > 0) { if ((strcasecmp(elem, NSSRC_COMPAT) == 0) || (strcasecmp(curdbt.srclist[0].name, NSSRC_COMPAT) == 0)) { - /* XXX: syslog the following */ - warnx("%s line %d: 'compat' used with other sources", + syslog(LOG_ERR, + "NSSWITCH(nsparser): %s line %d: 'compat' used with other sources", _PATH_NS_CONF, lineno); return; } } for (i = 0; i < curdbt.srclistsize; i++) { if (strcasecmp(curdbt.srclist[i].name, elem) == 0) { - /* XXX: syslog the following */ - warnx("%s line %d: duplicate source '%s'", + syslog(LOG_ERR, + "NSSWITCH(nsparser): %s line %d: duplicate source '%s'", _PATH_NS_CONF, lineno, elem); return; } diff --git a/lib/libc/net/nss_backends.h b/lib/libc/net/nss_backends.h new file mode 100644 index 0000000..9bea37b --- /dev/null +++ b/lib/libc/net/nss_backends.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was 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. + * + * $FreeBSD$ + */ +/* + * Eventually, the implementations of existing built-in NSS functions + * may be moved into NSS modules and live here. + */ +#if 0 +NSS_BACKEND( files, _files_nss_module_register ) +NSS_BACKEND( dns, _dns_nss_module_register ) +NSS_BACKEND( nis, _nis_nss_module_register ) +NSS_BACKEND( compat, _compat_nss_module_register ) +#endif diff --git a/lib/libc/net/nss_compat.c b/lib/libc/net/nss_compat.c new file mode 100644 index 0000000..8441103 --- /dev/null +++ b/lib/libc/net/nss_compat.c @@ -0,0 +1,270 @@ +/*- + * Copyright (c) 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was 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. + * + * Compatibility shims for the GNU C Library-style nsswitch interface. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/param.h> +#include <nss.h> +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.h" + + +struct group; +struct passwd; + +static int terminator; + +#define DECLARE_TERMINATOR(x) \ +static pthread_key_t _term_key_##x; \ +static void \ +_term_create_##x(void) \ +{ \ + (void)_pthread_key_create(&_term_key_##x, NULL); \ +} \ +static void *_term_main_##x; \ +static pthread_once_t _term_once_##x = PTHREAD_ONCE_INIT + +#define SET_TERMINATOR(x, y) \ +do { \ + if (_pthread_main_np()) \ + _term_main_##x = (y); \ + else { \ + (void)_pthread_once(&_term_once_##x, _term_create_##x); \ + (void)_pthread_setspecific(_term_key_##x, y); \ + } \ +} while (0) + +#define CHECK_TERMINATOR(x) \ +(_pthread_main_np() ? \ + (_term_main_##x) : \ + ((void)_pthread_once(&_term_once_##x, _term_create_##x), \ + _pthread_getspecific(_term_key_##x))) + + + +DECLARE_TERMINATOR(group); + + +int +__nss_compat_getgrnam_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(const char *, struct group *, char *, size_t, int *); + const char *name; + struct group *grp; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + fn = mdata; + name = va_arg(ap, const char *); + grp = va_arg(ap, struct group *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(name, grp, buffer, bufsize, errnop); + if (status == NS_SUCCESS) + *(struct group **)retval = grp; + return (__nss_compat_result(status)); +} + + +int +__nss_compat_getgrgid_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(gid_t, struct group *, char *, size_t, int *); + gid_t gid; + struct group *grp; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + fn = mdata; + gid = va_arg(ap, gid_t); + grp = va_arg(ap, struct group *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(gid, grp, buffer, bufsize, errnop); + if (status == NS_SUCCESS) + *(struct group **)retval = grp; + return (__nss_compat_result(status)); +} + + +int +__nss_compat_getgrent_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(struct group *, char *, size_t, int *); + struct group *grp; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + if (CHECK_TERMINATOR(group)) + return (NS_NOTFOUND); + fn = mdata; + grp = va_arg(ap, struct group *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(grp, buffer, bufsize, errnop); + if (status == NS_SUCCESS) + *(struct group **)retval = grp; + else + SET_TERMINATOR(group, &terminator); + return (__nss_compat_result(status)); +} + + +int +__nss_compat_setgrent(void *retval, void *mdata, va_list ap) +{ + + SET_TERMINATOR(group, NULL); + ((int (*)(void))mdata)(); + return (NS_UNAVAIL); +} + + +int +__nss_compat_endgrent(void *retval, void *mdata, va_list ap) +{ + + SET_TERMINATOR(group, NULL); + ((int (*)(void))mdata)(); + return (NS_UNAVAIL); +} + + + +DECLARE_TERMINATOR(passwd); + + +int +__nss_compat_getpwnam_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(const char *, struct passwd *, char *, size_t, int *); + const char *name; + struct passwd *pwd; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + fn = mdata; + name = va_arg(ap, const char *); + pwd = va_arg(ap, struct passwd *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(name, pwd, buffer, bufsize, errnop); + if (status == NS_SUCCESS) + *(struct passwd **)retval = pwd; + return (__nss_compat_result(status)); +} + + +int +__nss_compat_getpwuid_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(uid_t, struct passwd *, char *, size_t, int *); + uid_t uid; + struct passwd *pwd; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + fn = mdata; + uid = va_arg(ap, uid_t); + pwd = va_arg(ap, struct passwd *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(uid, pwd, buffer, bufsize, errnop); + if (status == NS_SUCCESS) + *(struct passwd **)retval = pwd; + return (__nss_compat_result(status)); +} + + +int +__nss_compat_getpwent_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(struct passwd *, char *, size_t, int *); + struct passwd *pwd; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + if (CHECK_TERMINATOR(passwd)) + return (NS_NOTFOUND); + fn = mdata; + pwd = va_arg(ap, struct passwd *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(pwd, buffer, bufsize, errnop); + if (status == NS_SUCCESS) + *(struct passwd **)retval = pwd; + else + SET_TERMINATOR(passwd, &terminator); + return (__nss_compat_result(status)); +} + + +int +__nss_compat_setpwent(void *retval, void *mdata, va_list ap) +{ + + SET_TERMINATOR(passwd, NULL); + ((int (*)(void))mdata)(); + return (NS_UNAVAIL); +} + + +int +__nss_compat_endpwent(void *retval, void *mdata, va_list ap) +{ + + SET_TERMINATOR(passwd, NULL); + ((int (*)(void))mdata)(); + return (NS_UNAVAIL); +} |