1/* Global list of NSS service modules.
2 Copyright (c) 2020-2024 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. */
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 SHARED && defined USE_NSCD
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 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. */
147static bool
148module_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, bind: __nss_files_functions);
159}
160
161/* Loads the built-in nss_dns module. */
162static bool
163module_load_nss_dns (struct nss_module *module)
164{
165 return module_load_builtin (module, bind: __nss_dns_functions);
166}
167
168/* Internal implementation of __nss_module_load. */
169static bool
170module_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 (ptr: 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 (map: handle);
234 return false;
235 }
236 pointers[idx] = __libc_dlsym (map: handle, name: function_name);
237 free (ptr: 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 (map: 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. */
296bool
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
313static int
314name_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. */
322void *
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. */
350static void
351nss_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
363define_traced_file (pwd, _PATH_NSSWITCH_CONF);
364define_traced_file (grp, _PATH_NSSWITCH_CONF);
365define_traced_file (hst, _PATH_NSSWITCH_CONF);
366define_traced_file (serv, _PATH_NSSWITCH_CONF);
367define_traced_file (netgr, _PATH_NSSWITCH_CONF);
368
369/* Called by nscd and nscd alone. */
370void
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. */
407void
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
419void
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 (map: current->handle);
429
430 struct nss_module *next = current->next;
431 free (ptr: current);
432 current = next;
433 }
434 nss_module_list = NULL;
435}
436

source code of glibc/nss/nss_module.c