diff options
Diffstat (limited to 'nss')
-rw-r--r-- | nss/Makefile | 2 | ||||
-rw-r--r-- | nss/function.def | 126 | ||||
-rw-r--r-- | nss/nss_module.c | 304 | ||||
-rw-r--r-- | nss/nss_module.h | 93 |
4 files changed, 464 insertions, 61 deletions
diff --git a/nss/Makefile b/nss/Makefile index 20c412c3e1..18035a4fb4 100644 --- a/nss/Makefile +++ b/nss/Makefile @@ -30,7 +30,7 @@ routines = nsswitch getnssent getnssent_r digits_dots \ $(addsuffix -lookup,$(databases)) \ compat-lookup nss_hash nss_files_fopen \ nss_readline nss_parse_line_result \ - nss_fgetent_r + nss_fgetent_r nss_module # These are the databases that go through nss dispatch. # Caution: if you add a database here, you must add its real name diff --git a/nss/function.def b/nss/function.def index b44eb77d4f..e79c8cf153 100644 --- a/nss/function.def +++ b/nss/function.def @@ -1,4 +1,4 @@ -/* List of functions defined for static NSS in GNU C Library. +/* List of all functions defined for the NSS in GNU C Library. Copyright (C) 1996-2020 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -16,63 +16,69 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ -/* - This is a minimal config. Only services `files' and `dns' are supported. -*/ +/* This list must be kept sorted!!! */ -/* aliases */ -DEFINE_ENT (files, alias) -DEFINE_GETBY (files, alias, name) - -/* ethers */ -DEFINE_ENT (files, ether) - -/* group */ -DEFINE_ENT (files, gr) -DEFINE_GET (files, grgid) -DEFINE_GET (files, grnam) - -/* hosts */ -DEFINE_ENT (files, host) -DEFINE_GETBY (files, host, addr) -DEFINE_GETBY (files, host, name) -DEFINE_GETBY (files, host, name2) -DEFINE_GET (files, hostton) -DEFINE_GET (files, ntohost) -DEFINE_GETBY (dns, host, addr) -DEFINE_GETBY (dns, host, name) -DEFINE_GETBY (dns, host, name2) - -/* netgroup */ -DEFINE_ENT (files, netgr) - -/* networks */ -DEFINE_ENT (files, net) -DEFINE_GETBY (files, net, name) -DEFINE_GETBY (files, net, addr) -DEFINE_GETBY (dns, net, name) -DEFINE_GETBY (dns, net, addr) - -/* protocols */ -DEFINE_ENT (files, proto) -DEFINE_GETBY (files, proto, name) -DEFINE_GETBY (files, proto, number) - -/* passwd */ -DEFINE_ENT (files, pw) -DEFINE_GET (files, pwnam) -DEFINE_GET (files, pwuid) - -/* rpc */ -DEFINE_ENT (files, rpc) -DEFINE_GETBY (files, rpc, name) -DEFINE_GETBY (files, rpc, number) - -/* services */ -DEFINE_ENT (files, serv) -DEFINE_GETBY (files, serv, name) -DEFINE_GETBY (files, serv, port) - -/* shadow */ -DEFINE_ENT (files, sp) -DEFINE_GET (files, spnam) +DEFINE_NSS_FUNCTION (endaliasent) +DEFINE_NSS_FUNCTION (endetherent) +DEFINE_NSS_FUNCTION (endgrent) +DEFINE_NSS_FUNCTION (endhostent) +DEFINE_NSS_FUNCTION (endnetent) +DEFINE_NSS_FUNCTION (endnetgrent) +DEFINE_NSS_FUNCTION (endprotoent) +DEFINE_NSS_FUNCTION (endpwent) +DEFINE_NSS_FUNCTION (endrpcent) +DEFINE_NSS_FUNCTION (endservent) +DEFINE_NSS_FUNCTION (endsgent) +DEFINE_NSS_FUNCTION (endspent) +DEFINE_NSS_FUNCTION (getaliasbyname_r) +DEFINE_NSS_FUNCTION (getaliasent_r) +DEFINE_NSS_FUNCTION (getcanonname_r) +DEFINE_NSS_FUNCTION (getetherent_r) +DEFINE_NSS_FUNCTION (getgrent_r) +DEFINE_NSS_FUNCTION (getgrgid_r) +DEFINE_NSS_FUNCTION (getgrnam_r) +DEFINE_NSS_FUNCTION (gethostbyaddr2_r) +DEFINE_NSS_FUNCTION (gethostbyaddr_r) +DEFINE_NSS_FUNCTION (gethostbyname2_r) +DEFINE_NSS_FUNCTION (gethostbyname3_r) +DEFINE_NSS_FUNCTION (gethostbyname4_r) +DEFINE_NSS_FUNCTION (gethostbyname_r) +DEFINE_NSS_FUNCTION (gethostent_r) +DEFINE_NSS_FUNCTION (gethostton_r) +DEFINE_NSS_FUNCTION (getnetbyaddr_r) +DEFINE_NSS_FUNCTION (getnetbyname_r) +DEFINE_NSS_FUNCTION (getnetent_r) +DEFINE_NSS_FUNCTION (getnetgrent_r) +DEFINE_NSS_FUNCTION (getntohost_r) +DEFINE_NSS_FUNCTION (getprotobyname_r) +DEFINE_NSS_FUNCTION (getprotobynumber_r) +DEFINE_NSS_FUNCTION (getprotoent_r) +DEFINE_NSS_FUNCTION (getpublickey) +DEFINE_NSS_FUNCTION (getpwent_r) +DEFINE_NSS_FUNCTION (getpwnam_r) +DEFINE_NSS_FUNCTION (getpwuid_r) +DEFINE_NSS_FUNCTION (getrpcbyname_r) +DEFINE_NSS_FUNCTION (getrpcbynumber_r) +DEFINE_NSS_FUNCTION (getrpcent_r) +DEFINE_NSS_FUNCTION (getsecretkey) +DEFINE_NSS_FUNCTION (getservbyname_r) +DEFINE_NSS_FUNCTION (getservbyport_r) +DEFINE_NSS_FUNCTION (getservent_r) +DEFINE_NSS_FUNCTION (getsgent_r) +DEFINE_NSS_FUNCTION (getsgnam_r) +DEFINE_NSS_FUNCTION (getspent_r) +DEFINE_NSS_FUNCTION (getspnam_r) +DEFINE_NSS_FUNCTION (initgroups_dyn) +DEFINE_NSS_FUNCTION (netname2user) +DEFINE_NSS_FUNCTION (setaliasent) +DEFINE_NSS_FUNCTION (setetherent) +DEFINE_NSS_FUNCTION (setgrent) +DEFINE_NSS_FUNCTION (sethostent) +DEFINE_NSS_FUNCTION (setnetent) +DEFINE_NSS_FUNCTION (setnetgrent) +DEFINE_NSS_FUNCTION (setprotoent) +DEFINE_NSS_FUNCTION (setpwent) +DEFINE_NSS_FUNCTION (setrpcent) +DEFINE_NSS_FUNCTION (setservent) +DEFINE_NSS_FUNCTION (setsgent) +DEFINE_NSS_FUNCTION (setspent) diff --git a/nss/nss_module.c b/nss/nss_module.c new file mode 100644 index 0000000000..8de8db09c3 --- /dev/null +++ b/nss/nss_module.c @@ -0,0 +1,304 @@ +/* Global list of NSS service modules. + Copyright (c) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <nss_module.h> + +#include <array_length.h> +#include <assert.h> +#include <atomic.h> +#include <dlfcn.h> +#include <gnu/lib-names.h> +#include <libc-lock.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef LINK_OBSOLETE_NSL +# define DEFAULT_CONFIG "compat [NOTFOUND=return] files" +# define DEFAULT_DEFCONFIG "nis [NOTFOUND=return] files" +#else +# define DEFAULT_CONFIG "files" +# define DEFAULT_DEFCONFIG "files" +#endif + +/* Suffix after .so of NSS service modules. This is a bit of magic, + but we assume LIBNSS_FILES_SO looks like "libnss_files.so.2" and we + want a pointer to the ".2" part. We have no API to extract this + except through the auto-generated lib-names.h and some static + pointer manipulation. The "-1" accounts for the trailing NUL + included in the sizeof. */ +static const char *const __nss_shlib_revision + = LIBNSS_FILES_SO + sizeof("libnss_files.so") - 1; + +/* A single-linked list used to implement a mapping from service names + to NSS modules. (Most systems only use five or so modules, so a + list is sufficient here.) Elements of this list are never freed + during normal operation. */ +static struct nss_module *nss_module_list; + +/* Covers the list and also loading of individual NSS service + modules. */ +__libc_lock_define (static, nss_module_list_lock); + +#if defined USE_NSCD && (!defined DO_STATIC_NSS || defined SHARED) +/* Nonzero if this is the nscd process. */ +static bool is_nscd; +/* The callback passed to the init functions when nscd is used. */ +static void (*nscd_init_cb) (size_t, struct traced_file *); +#endif + +/* Allocate the service NAME with length NAME_LENGTH. If the service + is already allocated in the nss_module_list cache then we return a + pointer to the struct nss_module, otherwise we try to allocate a + new struct nss_module entry and add it to the global + nss_modules_list cache. If we fail to allocate the entry we return + NULL. Failure to allocate the entry is always transient. */ +struct nss_module * +__nss_module_allocate (const char *name, size_t name_length) +{ + __libc_lock_lock (nss_module_list_lock); + + struct nss_module *result = NULL; + for (struct nss_module *p = nss_module_list; p != NULL; p = p->next) + if (strncmp (p->name, name, name_length) == 0 + && p->name[name_length] == '\0') + { + /* Return the previously existing object. */ + result = p; + break; + } + + if (result == NULL) + { + /* Allocate a new list entry if the name was not found in the + list. */ + result = malloc (sizeof (*result) + name_length + 1); + if (result != NULL) + { + result->state = nss_module_uninitialized; + memcpy (result->name, name, name_length); + result->name[name_length] = '\0'; + result->handle = NULL; + result->next = nss_module_list; + nss_module_list = result; + } + } + + __libc_lock_unlock (nss_module_list_lock); + return result; +} + +/* Long enough to store the name of any function in the + nss_function_name_array list below, as getprotobynumber_r is the + longest entry in that list. */ +typedef char function_name[sizeof("getprotobynumber_r")]; + +static const function_name nss_function_name_array[] = + { +#undef DEFINE_NSS_FUNCTION +#define DEFINE_NSS_FUNCTION(x) #x, +#include "function.def" + }; + +/* Internal implementation of __nss_module_load. */ +static bool +module_load (struct nss_module *module) +{ + void *handle; + { + char *shlib_name; + if (__asprintf (&shlib_name, "libnss_%s.so%s", + module->name, __nss_shlib_revision) < 0) + /* This is definitely a temporary failure. Do not update + module->state. This will trigger another attempt at the next + call. */ + return false; + + handle = __libc_dlopen (shlib_name); + free (shlib_name); + } + + /* Failing to load the module can be caused by several different + scenarios. One such scenario is that the module has been removed + from the disk. In which case the in-memory version is all that + we have, and if the module->state indidates it is loaded then we + can use it. */ + if (handle == NULL) + { + /* dlopen failure. We do not know if this a temporary or + permanent error. See bug 22041. Update the state using the + double-checked locking idiom. */ + + __libc_lock_lock (nss_module_list_lock); + bool result = result; + switch ((enum nss_module_state) atomic_load_acquire (&module->state)) + { + case nss_module_uninitialized: + atomic_store_release (&module->state, nss_module_failed); + result = false; + break; + case nss_module_loaded: + result = true; + break; + case nss_module_failed: + result = false; + break; + } + __libc_lock_unlock (nss_module_list_lock); + return result; + } + + nss_module_functions_untyped pointers; + + /* Look up and store locally all the function pointers we may need + later. Doing this now means the data will not change in the + future. */ + for (size_t idx = 0; idx < array_length (nss_function_name_array); ++idx) + { + char *function_name; + if (__asprintf (&function_name, "_nss_%s_%s", + module->name, nss_function_name_array[idx]) < 0) + { + /* Definitely a temporary error. */ + __libc_dlclose (handle); + return false; + } + pointers[idx] = __libc_dlsym (handle, function_name); + free (function_name); +#ifdef PTR_MANGLE + PTR_MANGLE (pointers[idx]); +#endif + } + +# ifdef USE_NSCD + if (is_nscd) + { + /* Call the init function when nscd is used. */ + size_t initlen = (5 + strlen (module->name) + + strlen ("_init") + 1); + char init_name[initlen]; + + /* Construct the init function name. */ + __stpcpy (__stpcpy (__stpcpy (init_name, + "_nss_"), + module->name), + "_init"); + + /* Find the optional init function. */ + void (*ifct) (void (*) (size_t, struct traced_file *)) + = __libc_dlsym (handle, init_name); + if (ifct != NULL) + { + void (*cb) (size_t, struct traced_file *) = nscd_init_cb; +# ifdef PTR_DEMANGLE + PTR_DEMANGLE (cb); +# endif + ifct (cb); + } + } +# endif + + /* Install the function pointers, following the double-checked + locking idiom. Delay this after all processing, in case loading + the module triggers unwinding. */ + __libc_lock_lock (nss_module_list_lock); + switch ((enum nss_module_state) atomic_load_acquire (&module->state)) + { + case nss_module_uninitialized: + case nss_module_failed: + memcpy (module->functions.untyped, pointers, + sizeof (module->functions.untyped)); + module->handle = handle; + /* Synchronizes with unlocked __nss_module_load atomic_load_acquire. */ + atomic_store_release (&module->state, nss_module_loaded); + break; + case nss_module_loaded: + /* If the module was already loaded, close our own handle. This + does not actually unload the modules, only the reference + counter is decremented for the loaded module. */ + __libc_dlclose (handle); + break; + } + __libc_lock_unlock (nss_module_list_lock); + return true; +} + +/* Force the module identified by MODULE to be loaded. We return + false if the module could not be loaded, true otherwise. Loading + the module requires looking up all the possible interface APIs and + caching the results. */ +bool +__nss_module_load (struct nss_module *module) +{ + switch ((enum nss_module_state) atomic_load_acquire (&module->state)) + { + case nss_module_uninitialized: + return module_load (module); + case nss_module_loaded: + /* Loading has already succeeded. */ + return true; + case nss_module_failed: + /* Loading previously failed. */ + return false; + } + __builtin_unreachable (); +} + +static int +name_search (const void *left, const void *right) +{ + return strcmp (left, right); +} + +/* Load module MODULE (if it isn't already) and return a pointer to + the module's implementation of NAME, otherwise return NULL on + failure or error. */ +void * +__nss_module_get_function (struct nss_module *module, const char *name) +{ + if (!__nss_module_load (module)) + return NULL; + + function_name *name_entry = bsearch (name, nss_function_name_array, + array_length (nss_function_name_array), + sizeof (function_name), name_search); + assert (name_entry != NULL); + size_t idx = name_entry - nss_function_name_array; + void *fptr = module->functions.untyped[idx]; +#ifdef PTR_DEMANGLE + PTR_DEMANGLE (fptr); +#endif + return fptr; +} + +void __libc_freeres_fn_section +__nss_module_freeres (void) +{ + struct nss_module *current = nss_module_list; + while (current != NULL) + { + if (current->state == nss_module_loaded) + __libc_dlclose (current->handle); + + struct nss_module *next = current->next; + free (current); + current = next; + } + nss_module_list = NULL; +} diff --git a/nss/nss_module.h b/nss/nss_module.h new file mode 100644 index 0000000000..17c46ea7db --- /dev/null +++ b/nss/nss_module.h @@ -0,0 +1,93 @@ +/* Global list of NSS service modules. + Copyright (c) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#ifndef _NSS_MODULE_H +#define _NSS_MODULE_H + +#include <nss.h> +#include <stdbool.h> + +/* See nss_database.h for a summary of how this relates. */ + +/* Typed function pointers for all functions that can be defined by a + service module. */ +struct nss_module_functions +{ +#undef DEFINE_NSS_FUNCTION +#define DEFINE_NSS_FUNCTION(f) nss_##f *f; +#include "function.def" +}; + +/* Untyped version of struct nss_module_functions, for consistent + processing purposes. */ +typedef void *nss_module_functions_untyped[sizeof (struct nss_module_functions) + / sizeof (void *)]; + +/* Initialization state of a NSS module. */ +enum nss_module_state +{ + nss_module_uninitialized, + nss_module_loaded, + nss_module_failed, +}; + +/* A NSS service module (potentially unloaded). Client code should + use the functions below. */ +struct nss_module +{ + /* Actual type is enum nss_module_state. Use int due to atomic + access. Used in a double-checked locking idiom. */ + int state; + + /* The function pointers in the module. */ + union + { + struct nss_module_functions typed; + nss_module_functions_untyped untyped; + } functions; + + /* Only used for __libc_freeres unloading. */ + void *handle; + + /* The next module in the list. */ + struct nss_module *next; + + /* The name of the module (as it appears in /etc/nsswitch.conf). */ + char name[]; +}; + +/* Allocates the NSS module NAME (of NAME_LENGTH bytes) and places it + into the global list. If it already exists in the list, return the + pre-existing module. This does not actually load the module. + Returns NULL on memory allocation failure. */ +struct nss_module *__nss_module_allocate (const char *name, + size_t name_length) attribute_hidden; + +/* Ensures that MODULE is in a loaded or failed state. */ +bool __nss_module_load (struct nss_module *module) attribute_hidden; + +/* Ensures that MODULE is loaded and returns a pointer to the function + NAME defined in it. Returns NULL if MODULE could not be loaded, or + if the function NAME is not defined in the module. */ +void *__nss_module_get_function (struct nss_module *module, const char *name) + attribute_hidden; + +/* Called from __libc_freeres. */ +void __nss_module_freeres (void) attribute_hidden; + +#endif /* NSS_MODULE_H */ |