about summary refs log tree commit diff
path: root/nss
diff options
context:
space:
mode:
Diffstat (limited to 'nss')
-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
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 */