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. */
43static 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. */
50static 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. */
58static bool is_nscd;
59/* The callback passed to the init functions when nscd is used. */
60static 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. */
69struct 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 (size: 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. */
107typedef char function_name[sizeof("getprotobynumber_r")];
108
109static 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. */
118static bool
119module_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. */
149static bool
150module_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, bind: __nss_files_functions);
163}
164
165/* Loads the built-in nss_dns module. */
166static bool
167module_load_nss_dns (struct nss_module *module)
168{
169 return module_load_builtin (module, bind: __nss_dns_functions);
170}
171
172/* Internal implementation of __nss_module_load. */
173static bool
174module_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 (ptr: 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 (map: handle);
238 return false;
239 }
240 pointers[idx] = __libc_dlsym (map: handle, name: function_name);
241 free (ptr: 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 (map: handle, name: 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 (map: 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. */
304bool
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
321static int
322name_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. */
330void *
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. */
360static void
361nss_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
373define_traced_file (pwd, _PATH_NSSWITCH_CONF);
374define_traced_file (grp, _PATH_NSSWITCH_CONF);
375define_traced_file (hst, _PATH_NSSWITCH_CONF);
376define_traced_file (serv, _PATH_NSSWITCH_CONF);
377define_traced_file (netgr, _PATH_NSSWITCH_CONF);
378
379/* Called by nscd and nscd alone. */
380void
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. */
419void
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
431void __libc_freeres_fn_section
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 (map: current->handle);
441
442 struct nss_module *next = current->next;
443 free (ptr: current);
444 current = next;
445 }
446 nss_module_list = NULL;
447}
448

source code of glibc/nss/nss_module.c