| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- /* Global list of NSS service modules.
- Copyright (c) 2020-2026 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 <nsswitch.h>
- #include <nscd/nscd.h>
- #include <nscd/nscd_proto.h>
- #include <array_length.h>
- #include <assert.h>
- #include <atomic.h>
- #include <dlfcn.h>
- #include <gnu/lib-names.h>
- #include <libc-lock.h>
- #include <nss_dns.h>
- #include <nss_files.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <pointer_guard.h>
- /* 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 SHARED && defined USE_NSCD
- /* 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"
- };
- /* Loads a built-in module, binding the symbols using the supplied
- callback function. Always returns true. */
- static bool
- module_load_builtin (struct nss_module *module,
- void (*bind) (nss_module_functions_untyped))
- {
- /* Initialize the function pointers, following the double-checked
- locking idiom. */
- __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:
- bind (module->functions.untyped);
- for (int i = 0; i < nss_module_functions_count; ++i)
- PTR_MANGLE (module->functions.untyped[i]);
- module->handle = NULL;
- /* Synchronizes with unlocked __nss_module_load atomic_load_acquire. */
- atomic_store_release (&module->state, nss_module_loaded);
- break;
- case nss_module_loaded:
- /* Nothing to clean up. */
- break;
- }
- __libc_lock_unlock (nss_module_list_lock);
- return true;
- }
- /* Loads the built-in nss_files module. */
- static bool
- module_load_nss_files (struct nss_module *module)
- {
- #if defined SHARED && defined USE_NSCD
- if (is_nscd)
- {
- void (*cb) (size_t, struct traced_file *) = nscd_init_cb;
- PTR_DEMANGLE (cb);
- _nss_files_init (cb);
- }
- #endif
- return module_load_builtin (module, __nss_files_functions);
- }
- /* Loads the built-in nss_dns module. */
- static bool
- module_load_nss_dns (struct nss_module *module)
- {
- return module_load_builtin (module, __nss_dns_functions);
- }
- /* Internal implementation of __nss_module_load. */
- static bool
- module_load (struct nss_module *module)
- {
- if (strcmp (module->name, "files") == 0)
- return module_load_nss_files (module);
- if (strcmp (module->name, "dns") == 0)
- return module_load_nss_dns (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);
- PTR_MANGLE (pointers[idx]);
- }
- # if defined SHARED && defined 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;
- PTR_DEMANGLE (cb);
- 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)
- {
- /* A successful dlopen might clobber errno. */
- int saved_errno = errno;
- if (!__nss_module_load (module))
- {
- /* Reporting module load failure is currently inaccurate. See
- bug 22041. Not changing errno is the conservative choice. */
- __set_errno (saved_errno);
- return NULL;
- }
- __set_errno (saved_errno);
- 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];
- PTR_DEMANGLE (fptr);
- return fptr;
- }
- #if defined SHARED && defined USE_NSCD
- /* Load all libraries for the service. */
- static void
- nss_load_all_libraries (enum nss_database service)
- {
- nss_action_list ni = NULL;
- if (__nss_database_get (service, &ni))
- while (ni->module != NULL)
- {
- __nss_module_load (ni->module);
- ++ni;
- }
- }
- define_traced_file (pwd, _PATH_NSSWITCH_CONF);
- define_traced_file (grp, _PATH_NSSWITCH_CONF);
- define_traced_file (hst, _PATH_NSSWITCH_CONF);
- define_traced_file (serv, _PATH_NSSWITCH_CONF);
- define_traced_file (netgr, _PATH_NSSWITCH_CONF);
- /* Called by nscd and nscd alone. */
- void
- __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
- {
- void (*cb1) (size_t, struct traced_file *);
- cb1 = cb;
- PTR_MANGLE (cb);
- nscd_init_cb = cb;
- is_nscd = true;
- /* Find all the relevant modules so that the init functions are called. */
- nss_load_all_libraries (nss_database_passwd);
- nss_load_all_libraries (nss_database_group);
- nss_load_all_libraries (nss_database_hosts);
- nss_load_all_libraries (nss_database_services);
- /* Make sure NSCD purges its cache if nsswitch.conf changes. */
- init_traced_file (&pwd_traced_file.file, _PATH_NSSWITCH_CONF, 0);
- cb1 (pwddb, &pwd_traced_file.file);
- init_traced_file (&grp_traced_file.file, _PATH_NSSWITCH_CONF, 0);
- cb1 (grpdb, &grp_traced_file.file);
- init_traced_file (&hst_traced_file.file, _PATH_NSSWITCH_CONF, 0);
- cb1 (hstdb, &hst_traced_file.file);
- init_traced_file (&serv_traced_file.file, _PATH_NSSWITCH_CONF, 0);
- cb1 (servdb, &serv_traced_file.file);
- init_traced_file (&netgr_traced_file.file, _PATH_NSSWITCH_CONF, 0);
- cb1 (netgrdb, &netgr_traced_file.file);
- /* Disable all uses of NSCD. */
- __nss_not_use_nscd_passwd = -1;
- __nss_not_use_nscd_group = -1;
- __nss_not_use_nscd_hosts = -1;
- __nss_not_use_nscd_services = -1;
- __nss_not_use_nscd_netgroup = -1;
- }
- #endif
- /* Block attempts to dlopen any module we haven't already opened. */
- void
- __nss_module_disable_loading (void)
- {
- __libc_lock_lock (nss_module_list_lock);
- for (struct nss_module *p = nss_module_list; p != NULL; p = p->next)
- if (p->state == nss_module_uninitialized)
- p->state = nss_module_failed;
- __libc_lock_unlock (nss_module_list_lock);
- }
- void
- __nss_module_freeres (void)
- {
- struct nss_module *current = nss_module_list;
- while (current != NULL)
- {
- /* Ignore built-in modules (which have a NULL handle). */
- if (current->state == nss_module_loaded
- && current->handle != NULL)
- __libc_dlclose (current->handle);
- struct nss_module *next = current->next;
- free (current);
- current = next;
- }
- nss_module_list = NULL;
- }
|