summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--malloc/set-freeres.c3
-rw-r--r--nss/Makefile2
-rw-r--r--nss/function.def126
-rw-r--r--nss/nss_module.c304
-rw-r--r--nss/nss_module.h93
5 files changed, 467 insertions, 61 deletions
diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
index aa80eb64b8..b328cca7c6 100644
--- a/malloc/set-freeres.c
+++ b/malloc/set-freeres.c
@@ -20,6 +20,7 @@
#include <set-hooks.h>
#include <libc-internal.h>
+#include "../nss/nss_module.h"
#include "../libio/libioP.h"
DEFINE_HOOK (__libc_subfreeres, (void));
@@ -41,6 +42,8 @@ __libc_freeres (void)
{
void *const *p;
+ call_function_static_weak (__nss_module_freeres);
+
_IO_cleanup ();
/* We run the resource freeing after IO cleanup. */
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 */