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 * |
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 | #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 |
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 |
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 |
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 |
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 |
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 * |
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 |
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 |
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 |
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 |
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 | |