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 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 * |
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 | #ifdef 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 | # ifdef 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 __libc_freeres_fn_section |
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 | } |
436 | |