(root)/
glibc-2.38/
nss/
nss_module.c
       1  /* Global list of NSS service modules.
       2     Copyright (c) 2020-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <nsswitch.h>
      20  #include <nscd/nscd.h>
      21  #include <nscd/nscd_proto.h>
      22  
      23  #include <array_length.h>
      24  #include <assert.h>
      25  #include <atomic.h>
      26  #include <dlfcn.h>
      27  #include <gnu/lib-names.h>
      28  #include <libc-lock.h>
      29  #include <nss_dns.h>
      30  #include <nss_files.h>
      31  #include <stddef.h>
      32  #include <stdio.h>
      33  #include <stdlib.h>
      34  #include <string.h>
      35  #include <pointer_guard.h>
      36  
      37  /* Suffix after .so of NSS service modules.  This is a bit of magic,
      38     but we assume LIBNSS_FILES_SO looks like "libnss_files.so.2" and we
      39     want a pointer to the ".2" part.  We have no API to extract this
      40     except through the auto-generated lib-names.h and some static
      41     pointer manipulation.  The "-1" accounts for the trailing NUL
      42     included in the sizeof.  */
      43  static const char *const __nss_shlib_revision
      44  	= LIBNSS_FILES_SO + sizeof("libnss_files.so") - 1;
      45  
      46  /* A single-linked list used to implement a mapping from service names
      47     to NSS modules.  (Most systems only use five or so modules, so a
      48     list is sufficient here.)  Elements of this list are never freed
      49     during normal operation.  */
      50  static struct nss_module *nss_module_list;
      51  
      52  /* Covers the list and also loading of individual NSS service
      53     modules.  */
      54  __libc_lock_define (static, nss_module_list_lock);
      55  
      56  #if defined SHARED && defined USE_NSCD
      57  /* Nonzero if this is the nscd process.  */
      58  static bool is_nscd;
      59  /* The callback passed to the init functions when nscd is used.  */
      60  static void (*nscd_init_cb) (size_t, struct traced_file *);
      61  #endif
      62  
      63  /* Allocate the service NAME with length NAME_LENGTH.  If the service
      64     is already allocated in the nss_module_list cache then we return a
      65     pointer to the struct nss_module, otherwise we try to allocate a
      66     new struct nss_module entry and add it to the global
      67     nss_modules_list cache.  If we fail to allocate the entry we return
      68     NULL.  Failure to allocate the entry is always transient.  */
      69  struct nss_module *
      70  __nss_module_allocate (const char *name, size_t name_length)
      71  {
      72    __libc_lock_lock (nss_module_list_lock);
      73  
      74    struct nss_module *result = NULL;
      75    for (struct nss_module *p = nss_module_list; p != NULL; p = p->next)
      76      if (strncmp (p->name, name, name_length) == 0
      77          && p->name[name_length] == '\0')
      78        {
      79          /* Return the previously existing object.  */
      80          result = p;
      81          break;
      82        }
      83  
      84    if (result == NULL)
      85      {
      86        /* Allocate a new list entry if the name was not found in the
      87           list.  */
      88        result = malloc (sizeof (*result) + name_length + 1);
      89        if (result != NULL)
      90          {
      91            result->state = nss_module_uninitialized;
      92            memcpy (result->name, name, name_length);
      93            result->name[name_length] = '\0';
      94            result->handle = NULL;
      95            result->next = nss_module_list;
      96            nss_module_list = result;
      97          }
      98      }
      99  
     100    __libc_lock_unlock (nss_module_list_lock);
     101    return result;
     102  }
     103  
     104  /* Long enough to store the name of any function in the
     105     nss_function_name_array list below, as getprotobynumber_r is the
     106     longest entry in that list.  */
     107  typedef char function_name[sizeof("getprotobynumber_r")];
     108  
     109  static const function_name nss_function_name_array[] =
     110    {
     111  #undef DEFINE_NSS_FUNCTION
     112  #define DEFINE_NSS_FUNCTION(x) #x,
     113  #include "function.def"
     114    };
     115  
     116  /* Loads a built-in module, binding the symbols using the supplied
     117     callback function.  Always returns true.  */
     118  static bool
     119  module_load_builtin (struct nss_module *module,
     120  		     void (*bind) (nss_module_functions_untyped))
     121  {
     122    /* Initialize the function pointers, following the double-checked
     123       locking idiom.  */
     124    __libc_lock_lock (nss_module_list_lock);
     125    switch ((enum nss_module_state) atomic_load_acquire (&module->state))
     126      {
     127      case nss_module_uninitialized:
     128      case nss_module_failed:
     129        bind (module->functions.untyped);
     130  
     131        for (int i = 0; i < nss_module_functions_count; ++i)
     132  	PTR_MANGLE (module->functions.untyped[i]);
     133  
     134        module->handle = NULL;
     135        /* Synchronizes with unlocked __nss_module_load atomic_load_acquire.  */
     136        atomic_store_release (&module->state, nss_module_loaded);
     137        break;
     138      case nss_module_loaded:
     139        /* Nothing to clean up.  */
     140        break;
     141      }
     142    __libc_lock_unlock (nss_module_list_lock);
     143    return true;
     144  }
     145  
     146  /* Loads the built-in nss_files module.  */
     147  static bool
     148  module_load_nss_files (struct nss_module *module)
     149  {
     150  #if defined SHARED && defined USE_NSCD
     151    if (is_nscd)
     152      {
     153        void (*cb) (size_t, struct traced_file *) = nscd_init_cb;
     154        PTR_DEMANGLE (cb);
     155        _nss_files_init (cb);
     156      }
     157  #endif
     158    return module_load_builtin (module, __nss_files_functions);
     159  }
     160  
     161  /* Loads the built-in nss_dns module.  */
     162  static bool
     163  module_load_nss_dns (struct nss_module *module)
     164  {
     165    return module_load_builtin (module, __nss_dns_functions);
     166  }
     167  
     168  /* Internal implementation of __nss_module_load.  */
     169  static bool
     170  module_load (struct nss_module *module)
     171  {
     172    if (strcmp (module->name, "files") == 0)
     173      return module_load_nss_files (module);
     174    if (strcmp (module->name, "dns") == 0)
     175      return module_load_nss_dns (module);
     176  
     177    void *handle;
     178    {
     179      char *shlib_name;
     180      if (__asprintf (&shlib_name, "libnss_%s.so%s",
     181                      module->name, __nss_shlib_revision) < 0)
     182        /* This is definitely a temporary failure.  Do not update
     183           module->state.  This will trigger another attempt at the next
     184           call.  */
     185        return false;
     186  
     187      handle = __libc_dlopen (shlib_name);
     188      free (shlib_name);
     189    }
     190  
     191    /* Failing to load the module can be caused by several different
     192       scenarios.  One such scenario is that the module has been removed
     193       from the disk.  In which case the in-memory version is all that
     194       we have, and if the module->state indidates it is loaded then we
     195       can use it.  */
     196    if (handle == NULL)
     197      {
     198        /* dlopen failure.  We do not know if this a temporary or
     199           permanent error.  See bug 22041.  Update the state using the
     200           double-checked locking idiom.  */
     201  
     202        __libc_lock_lock (nss_module_list_lock);
     203        bool result = result;
     204        switch ((enum nss_module_state) atomic_load_acquire (&module->state))
     205          {
     206          case nss_module_uninitialized:
     207            atomic_store_release (&module->state, nss_module_failed);
     208            result = false;
     209            break;
     210          case nss_module_loaded:
     211            result = true;
     212            break;
     213          case nss_module_failed:
     214            result = false;
     215            break;
     216          }
     217        __libc_lock_unlock (nss_module_list_lock);
     218        return result;
     219      }
     220  
     221    nss_module_functions_untyped pointers;
     222  
     223    /* Look up and store locally all the function pointers we may need
     224       later.  Doing this now means the data will not change in the
     225       future.  */
     226    for (size_t idx = 0; idx < array_length (nss_function_name_array); ++idx)
     227      {
     228        char *function_name;
     229        if (__asprintf (&function_name, "_nss_%s_%s",
     230                        module->name, nss_function_name_array[idx]) < 0)
     231          {
     232            /* Definitely a temporary error.  */
     233            __libc_dlclose (handle);
     234            return false;
     235          }
     236        pointers[idx] = __libc_dlsym (handle, function_name);
     237        free (function_name);
     238        PTR_MANGLE (pointers[idx]);
     239      }
     240  
     241  # if defined SHARED && defined USE_NSCD
     242    if (is_nscd)
     243      {
     244        /* Call the init function when nscd is used.  */
     245        size_t initlen = (5 + strlen (module->name)
     246  			+ strlen ("_init") + 1);
     247        char init_name[initlen];
     248  
     249        /* Construct the init function name.  */
     250        __stpcpy (__stpcpy (__stpcpy (init_name,
     251  				    "_nss_"),
     252  			  module->name),
     253  		"_init");
     254  
     255        /* Find the optional init function.  */
     256        void (*ifct) (void (*) (size_t, struct traced_file *))
     257  	= __libc_dlsym (handle, init_name);
     258        if (ifct != NULL)
     259  	{
     260  	  void (*cb) (size_t, struct traced_file *) = nscd_init_cb;
     261  	  PTR_DEMANGLE (cb);
     262  	  ifct (cb);
     263  	}
     264      }
     265  # endif
     266  
     267    /* Install the function pointers, following the double-checked
     268       locking idiom.  Delay this after all processing, in case loading
     269       the module triggers unwinding.  */
     270    __libc_lock_lock (nss_module_list_lock);
     271    switch ((enum nss_module_state) atomic_load_acquire (&module->state))
     272      {
     273      case nss_module_uninitialized:
     274      case nss_module_failed:
     275        memcpy (module->functions.untyped, pointers,
     276                sizeof (module->functions.untyped));
     277        module->handle = handle;
     278        /* Synchronizes with unlocked __nss_module_load atomic_load_acquire.  */
     279        atomic_store_release (&module->state, nss_module_loaded);
     280        break;
     281      case nss_module_loaded:
     282        /* If the module was already loaded, close our own handle.  This
     283           does not actually unload the modules, only the reference
     284           counter is decremented for the loaded module.  */
     285        __libc_dlclose (handle);
     286        break;
     287      }
     288    __libc_lock_unlock (nss_module_list_lock);
     289    return true;
     290  }
     291  
     292  /* Force the module identified by MODULE to be loaded.  We return
     293     false if the module could not be loaded, true otherwise.  Loading
     294     the module requires looking up all the possible interface APIs and
     295     caching the results.  */
     296  bool
     297  __nss_module_load (struct nss_module *module)
     298  {
     299    switch ((enum nss_module_state) atomic_load_acquire (&module->state))
     300      {
     301      case nss_module_uninitialized:
     302        return module_load (module);
     303      case nss_module_loaded:
     304        /* Loading has already succeeded.  */
     305        return true;
     306      case nss_module_failed:
     307        /* Loading previously failed.  */
     308        return false;
     309      }
     310    __builtin_unreachable ();
     311  }
     312  
     313  static int
     314  name_search (const void *left, const void *right)
     315  {
     316    return strcmp (left, right);
     317  }
     318  
     319  /* Load module MODULE (if it isn't already) and return a pointer to
     320     the module's implementation of NAME, otherwise return NULL on
     321     failure or error.  */
     322  void *
     323  __nss_module_get_function (struct nss_module *module, const char *name)
     324  {
     325    /* A successful dlopen might clobber errno.   */
     326    int saved_errno = errno;
     327  
     328    if (!__nss_module_load (module))
     329      {
     330        /* Reporting module load failure is currently inaccurate.  See
     331  	 bug 22041.  Not changing errno is the conservative choice.  */
     332        __set_errno (saved_errno);
     333        return NULL;
     334      }
     335  
     336    __set_errno (saved_errno);
     337  
     338    function_name *name_entry = bsearch (name, nss_function_name_array,
     339                                         array_length (nss_function_name_array),
     340                                         sizeof (function_name), name_search);
     341    assert (name_entry != NULL);
     342    size_t idx = name_entry - nss_function_name_array;
     343    void *fptr = module->functions.untyped[idx];
     344    PTR_DEMANGLE (fptr);
     345    return fptr;
     346  }
     347  
     348  #if defined SHARED && defined USE_NSCD
     349  /* Load all libraries for the service.  */
     350  static void
     351  nss_load_all_libraries (enum nss_database service)
     352  {
     353    nss_action_list ni = NULL;
     354  
     355    if (__nss_database_get (service, &ni))
     356      while (ni->module != NULL)
     357        {
     358          __nss_module_load (ni->module);
     359          ++ni;
     360        }
     361  }
     362  
     363  define_traced_file (pwd, _PATH_NSSWITCH_CONF);
     364  define_traced_file (grp, _PATH_NSSWITCH_CONF);
     365  define_traced_file (hst, _PATH_NSSWITCH_CONF);
     366  define_traced_file (serv, _PATH_NSSWITCH_CONF);
     367  define_traced_file (netgr, _PATH_NSSWITCH_CONF);
     368  
     369  /* Called by nscd and nscd alone.  */
     370  void
     371  __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
     372  {
     373    void (*cb1) (size_t, struct traced_file *);
     374    cb1 = cb;
     375    PTR_MANGLE (cb);
     376    nscd_init_cb = cb;
     377    is_nscd = true;
     378  
     379    /* Find all the relevant modules so that the init functions are called.  */
     380    nss_load_all_libraries (nss_database_passwd);
     381    nss_load_all_libraries (nss_database_group);
     382    nss_load_all_libraries (nss_database_hosts);
     383    nss_load_all_libraries (nss_database_services);
     384  
     385    /* Make sure NSCD purges its cache if nsswitch.conf changes.  */
     386    init_traced_file (&pwd_traced_file.file, _PATH_NSSWITCH_CONF, 0);
     387    cb1 (pwddb, &pwd_traced_file.file);
     388    init_traced_file (&grp_traced_file.file, _PATH_NSSWITCH_CONF, 0);
     389    cb1 (grpdb, &grp_traced_file.file);
     390    init_traced_file (&hst_traced_file.file, _PATH_NSSWITCH_CONF, 0);
     391    cb1 (hstdb, &hst_traced_file.file);
     392    init_traced_file (&serv_traced_file.file, _PATH_NSSWITCH_CONF, 0);
     393    cb1 (servdb, &serv_traced_file.file);
     394    init_traced_file (&netgr_traced_file.file, _PATH_NSSWITCH_CONF, 0);
     395    cb1 (netgrdb, &netgr_traced_file.file);
     396  
     397    /* Disable all uses of NSCD.  */
     398    __nss_not_use_nscd_passwd = -1;
     399    __nss_not_use_nscd_group = -1;
     400    __nss_not_use_nscd_hosts = -1;
     401    __nss_not_use_nscd_services = -1;
     402    __nss_not_use_nscd_netgroup = -1;
     403  }
     404  #endif
     405  
     406  /* Block attempts to dlopen any module we haven't already opened.  */
     407  void
     408  __nss_module_disable_loading (void)
     409  {
     410    __libc_lock_lock (nss_module_list_lock);
     411  
     412    for (struct nss_module *p = nss_module_list; p != NULL; p = p->next)
     413      if (p->state == nss_module_uninitialized)
     414        p->state = nss_module_failed;
     415  
     416    __libc_lock_unlock (nss_module_list_lock);
     417  }
     418  
     419  void
     420  __nss_module_freeres (void)
     421  {
     422    struct nss_module *current = nss_module_list;
     423    while (current != NULL)
     424      {
     425        /* Ignore built-in modules (which have a NULL handle).  */
     426        if (current->state == nss_module_loaded
     427  	  && current->handle != NULL)
     428          __libc_dlclose (current->handle);
     429  
     430        struct nss_module *next = current->next;
     431        free (current);
     432        current = next;
     433      }
     434    nss_module_list = NULL;
     435  }