1 /* Global list of NSS service modules.
2    Copyright (c) 2020-2022 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 <sysdep.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 USE_NSCD && (!defined DO_STATIC_NSS || defined SHARED)
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 *
__nss_module_allocate(const char * name,size_t name_length)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
module_load_builtin(struct nss_module * module,void (* bind)(nss_module_functions_untyped))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 #ifdef PTR_MANGLE
132       for (int i = 0; i < nss_module_functions_count; ++i)
133 	PTR_MANGLE (module->functions.untyped[i]);
134 #endif
135 
136       module->handle = NULL;
137       /* Synchronizes with unlocked __nss_module_load atomic_load_acquire.  */
138       atomic_store_release (&module->state, nss_module_loaded);
139       break;
140     case nss_module_loaded:
141       /* Nothing to clean up.  */
142       break;
143     }
144   __libc_lock_unlock (nss_module_list_lock);
145   return true;
146 }
147 
148 /* Loads the built-in nss_files module.  */
149 static bool
module_load_nss_files(struct nss_module * module)150 module_load_nss_files (struct nss_module *module)
151 {
152 #ifdef USE_NSCD
153   if (is_nscd)
154     {
155       void (*cb) (size_t, struct traced_file *) = nscd_init_cb;
156 # ifdef PTR_DEMANGLE
157       PTR_DEMANGLE (cb);
158 # endif
159       _nss_files_init (cb);
160     }
161 #endif
162   return module_load_builtin (module, __nss_files_functions);
163 }
164 
165 /* Loads the built-in nss_dns module.  */
166 static bool
module_load_nss_dns(struct nss_module * module)167 module_load_nss_dns (struct nss_module *module)
168 {
169   return module_load_builtin (module, __nss_dns_functions);
170 }
171 
172 /* Internal implementation of __nss_module_load.  */
173 static bool
module_load(struct nss_module * module)174 module_load (struct nss_module *module)
175 {
176   if (strcmp (module->name, "files") == 0)
177     return module_load_nss_files (module);
178   if (strcmp (module->name, "dns") == 0)
179     return module_load_nss_dns (module);
180 
181   void *handle;
182   {
183     char *shlib_name;
184     if (__asprintf (&shlib_name, "libnss_%s.so%s",
185                     module->name, __nss_shlib_revision) < 0)
186       /* This is definitely a temporary failure.  Do not update
187          module->state.  This will trigger another attempt at the next
188          call.  */
189       return false;
190 
191     handle = __libc_dlopen (shlib_name);
192     free (shlib_name);
193   }
194 
195   /* Failing to load the module can be caused by several different
196      scenarios.  One such scenario is that the module has been removed
197      from the disk.  In which case the in-memory version is all that
198      we have, and if the module->state indidates it is loaded then we
199      can use it.  */
200   if (handle == NULL)
201     {
202       /* dlopen failure.  We do not know if this a temporary or
203          permanent error.  See bug 22041.  Update the state using the
204          double-checked locking idiom.  */
205 
206       __libc_lock_lock (nss_module_list_lock);
207       bool result = result;
208       switch ((enum nss_module_state) atomic_load_acquire (&module->state))
209         {
210         case nss_module_uninitialized:
211           atomic_store_release (&module->state, nss_module_failed);
212           result = false;
213           break;
214         case nss_module_loaded:
215           result = true;
216           break;
217         case nss_module_failed:
218           result = false;
219           break;
220         }
221       __libc_lock_unlock (nss_module_list_lock);
222       return result;
223     }
224 
225   nss_module_functions_untyped pointers;
226 
227   /* Look up and store locally all the function pointers we may need
228      later.  Doing this now means the data will not change in the
229      future.  */
230   for (size_t idx = 0; idx < array_length (nss_function_name_array); ++idx)
231     {
232       char *function_name;
233       if (__asprintf (&function_name, "_nss_%s_%s",
234                       module->name, nss_function_name_array[idx]) < 0)
235         {
236           /* Definitely a temporary error.  */
237           __libc_dlclose (handle);
238           return false;
239         }
240       pointers[idx] = __libc_dlsym (handle, function_name);
241       free (function_name);
242 #ifdef PTR_MANGLE
243       PTR_MANGLE (pointers[idx]);
244 #endif
245     }
246 
247 # ifdef USE_NSCD
248   if (is_nscd)
249     {
250       /* Call the init function when nscd is used.  */
251       size_t initlen = (5 + strlen (module->name)
252 			+ strlen ("_init") + 1);
253       char init_name[initlen];
254 
255       /* Construct the init function name.  */
256       __stpcpy (__stpcpy (__stpcpy (init_name,
257 				    "_nss_"),
258 			  module->name),
259 		"_init");
260 
261       /* Find the optional init function.  */
262       void (*ifct) (void (*) (size_t, struct traced_file *))
263 	= __libc_dlsym (handle, init_name);
264       if (ifct != NULL)
265 	{
266 	  void (*cb) (size_t, struct traced_file *) = nscd_init_cb;
267 #  ifdef PTR_DEMANGLE
268 	  PTR_DEMANGLE (cb);
269 #  endif
270 	  ifct (cb);
271 	}
272     }
273 # endif
274 
275   /* Install the function pointers, following the double-checked
276      locking idiom.  Delay this after all processing, in case loading
277      the module triggers unwinding.  */
278   __libc_lock_lock (nss_module_list_lock);
279   switch ((enum nss_module_state) atomic_load_acquire (&module->state))
280     {
281     case nss_module_uninitialized:
282     case nss_module_failed:
283       memcpy (module->functions.untyped, pointers,
284               sizeof (module->functions.untyped));
285       module->handle = handle;
286       /* Synchronizes with unlocked __nss_module_load atomic_load_acquire.  */
287       atomic_store_release (&module->state, nss_module_loaded);
288       break;
289     case nss_module_loaded:
290       /* If the module was already loaded, close our own handle.  This
291          does not actually unload the modules, only the reference
292          counter is decremented for the loaded module.  */
293       __libc_dlclose (handle);
294       break;
295     }
296   __libc_lock_unlock (nss_module_list_lock);
297   return true;
298 }
299 
300 /* Force the module identified by MODULE to be loaded.  We return
301    false if the module could not be loaded, true otherwise.  Loading
302    the module requires looking up all the possible interface APIs and
303    caching the results.  */
304 bool
__nss_module_load(struct nss_module * module)305 __nss_module_load (struct nss_module *module)
306 {
307   switch ((enum nss_module_state) atomic_load_acquire (&module->state))
308     {
309     case nss_module_uninitialized:
310       return module_load (module);
311     case nss_module_loaded:
312       /* Loading has already succeeded.  */
313       return true;
314     case nss_module_failed:
315       /* Loading previously failed.  */
316       return false;
317     }
318   __builtin_unreachable ();
319 }
320 
321 static int
name_search(const void * left,const void * right)322 name_search (const void *left, const void *right)
323 {
324   return strcmp (left, right);
325 }
326 
327 /* Load module MODULE (if it isn't already) and return a pointer to
328    the module's implementation of NAME, otherwise return NULL on
329    failure or error.  */
330 void *
__nss_module_get_function(struct nss_module * module,const char * name)331 __nss_module_get_function (struct nss_module *module, const char *name)
332 {
333   /* A successful dlopen might clobber errno.   */
334   int saved_errno = errno;
335 
336   if (!__nss_module_load (module))
337     {
338       /* Reporting module load failure is currently inaccurate.  See
339 	 bug 22041.  Not changing errno is the conservative choice.  */
340       __set_errno (saved_errno);
341       return NULL;
342     }
343 
344   __set_errno (saved_errno);
345 
346   function_name *name_entry = bsearch (name, nss_function_name_array,
347                                        array_length (nss_function_name_array),
348                                        sizeof (function_name), name_search);
349   assert (name_entry != NULL);
350   size_t idx = name_entry - nss_function_name_array;
351   void *fptr = module->functions.untyped[idx];
352 #ifdef PTR_DEMANGLE
353   PTR_DEMANGLE (fptr);
354 #endif
355   return fptr;
356 }
357 
358 #if defined SHARED && defined USE_NSCD
359 /* Load all libraries for the service.  */
360 static void
nss_load_all_libraries(enum nss_database service)361 nss_load_all_libraries (enum nss_database service)
362 {
363   nss_action_list ni = NULL;
364 
365   if (__nss_database_get (service, &ni))
366     while (ni->module != NULL)
367       {
368         __nss_module_load (ni->module);
369         ++ni;
370       }
371 }
372 
373 define_traced_file (pwd, _PATH_NSSWITCH_CONF);
374 define_traced_file (grp, _PATH_NSSWITCH_CONF);
375 define_traced_file (hst, _PATH_NSSWITCH_CONF);
376 define_traced_file (serv, _PATH_NSSWITCH_CONF);
377 define_traced_file (netgr, _PATH_NSSWITCH_CONF);
378 
379 /* Called by nscd and nscd alone.  */
380 void
__nss_disable_nscd(void (* cb)(size_t,struct traced_file *))381 __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
382 {
383   void (*cb1) (size_t, struct traced_file *);
384   cb1 = cb;
385 # ifdef PTR_MANGLE
386   PTR_MANGLE (cb);
387 # endif
388   nscd_init_cb = cb;
389   is_nscd = true;
390 
391   /* Find all the relevant modules so that the init functions are called.  */
392   nss_load_all_libraries (nss_database_passwd);
393   nss_load_all_libraries (nss_database_group);
394   nss_load_all_libraries (nss_database_hosts);
395   nss_load_all_libraries (nss_database_services);
396 
397   /* Make sure NSCD purges its cache if nsswitch.conf changes.  */
398   init_traced_file (&pwd_traced_file.file, _PATH_NSSWITCH_CONF, 0);
399   cb1 (pwddb, &pwd_traced_file.file);
400   init_traced_file (&grp_traced_file.file, _PATH_NSSWITCH_CONF, 0);
401   cb1 (grpdb, &grp_traced_file.file);
402   init_traced_file (&hst_traced_file.file, _PATH_NSSWITCH_CONF, 0);
403   cb1 (hstdb, &hst_traced_file.file);
404   init_traced_file (&serv_traced_file.file, _PATH_NSSWITCH_CONF, 0);
405   cb1 (servdb, &serv_traced_file.file);
406   init_traced_file (&netgr_traced_file.file, _PATH_NSSWITCH_CONF, 0);
407   cb1 (netgrdb, &netgr_traced_file.file);
408 
409   /* Disable all uses of NSCD.  */
410   __nss_not_use_nscd_passwd = -1;
411   __nss_not_use_nscd_group = -1;
412   __nss_not_use_nscd_hosts = -1;
413   __nss_not_use_nscd_services = -1;
414   __nss_not_use_nscd_netgroup = -1;
415 }
416 #endif
417 
418 /* Block attempts to dlopen any module we haven't already opened.  */
419 void
__nss_module_disable_loading(void)420 __nss_module_disable_loading (void)
421 {
422   __libc_lock_lock (nss_module_list_lock);
423 
424   for (struct nss_module *p = nss_module_list; p != NULL; p = p->next)
425     if (p->state == nss_module_uninitialized)
426       p->state = nss_module_failed;
427 
428   __libc_lock_unlock (nss_module_list_lock);
429 }
430 
431 void __libc_freeres_fn_section
__nss_module_freeres(void)432 __nss_module_freeres (void)
433 {
434   struct nss_module *current = nss_module_list;
435   while (current != NULL)
436     {
437       /* Ignore built-in modules (which have a NULL handle).  */
438       if (current->state == nss_module_loaded
439 	  && current->handle != NULL)
440         __libc_dlclose (current->handle);
441 
442       struct nss_module *next = current->next;
443       free (current);
444       current = next;
445     }
446   nss_module_list = NULL;
447 }
448