]>
Commit | Line | Data |
---|---|---|
171689da | 1 | /* Global list of NSS service modules. |
6d7e8eda | 2 | Copyright (c) 2020-2023 Free Software Foundation, Inc. |
171689da FW |
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 | ||
f8847d83 DD |
19 | #include <nsswitch.h> |
20 | #include <nscd/nscd.h> | |
21 | #include <nscd/nscd_proto.h> | |
171689da FW |
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> | |
ee5ed999 FW |
29 | #include <nss_dns.h> |
30 | #include <nss_files.h> | |
171689da FW |
31 | #include <stddef.h> |
32 | #include <stdio.h> | |
33 | #include <stdlib.h> | |
34 | #include <string.h> | |
88f4b692 | 35 | #include <pointer_guard.h> |
171689da | 36 | |
171689da FW |
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 | ||
bea1a4a7 | 56 | #if defined SHARED && defined USE_NSCD |
171689da FW |
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 | ||
ee5ed999 FW |
116 | /* Loads a built-in module, binding the symbols using the supplied |
117 | callback function. Always returns true. */ | |
f9c8b11e | 118 | static bool |
ee5ed999 FW |
119 | module_load_builtin (struct nss_module *module, |
120 | void (*bind) (nss_module_functions_untyped)) | |
f9c8b11e | 121 | { |
f9c8b11e FW |
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: | |
ee5ed999 FW |
129 | bind (module->functions.untyped); |
130 | ||
ee5ed999 FW |
131 | for (int i = 0; i < nss_module_functions_count; ++i) |
132 | PTR_MANGLE (module->functions.untyped[i]); | |
ee5ed999 | 133 | |
f9c8b11e FW |
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 | ||
ee5ed999 FW |
146 | /* Loads the built-in nss_files module. */ |
147 | static bool | |
148 | module_load_nss_files (struct nss_module *module) | |
149 | { | |
bea1a4a7 | 150 | #if defined SHARED && defined USE_NSCD |
ee5ed999 FW |
151 | if (is_nscd) |
152 | { | |
153 | void (*cb) (size_t, struct traced_file *) = nscd_init_cb; | |
ee5ed999 | 154 | PTR_DEMANGLE (cb); |
ee5ed999 FW |
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 | ||
171689da FW |
168 | /* Internal implementation of __nss_module_load. */ |
169 | static bool | |
170 | module_load (struct nss_module *module) | |
171 | { | |
f9c8b11e FW |
172 | if (strcmp (module->name, "files") == 0) |
173 | return module_load_nss_files (module); | |
ee5ed999 FW |
174 | if (strcmp (module->name, "dns") == 0) |
175 | return module_load_nss_dns (module); | |
f9c8b11e | 176 | |
171689da FW |
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); | |
171689da | 238 | PTR_MANGLE (pointers[idx]); |
171689da FW |
239 | } |
240 | ||
bea1a4a7 | 241 | # if defined SHARED && defined USE_NSCD |
171689da FW |
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; | |
171689da | 261 | PTR_DEMANGLE (cb); |
171689da FW |
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 | { | |
9bdf92c7 FW |
325 | /* A successful dlopen might clobber errno. */ |
326 | int saved_errno = errno; | |
327 | ||
171689da | 328 | if (!__nss_module_load (module)) |
9bdf92c7 FW |
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); | |
171689da FW |
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]; | |
171689da | 344 | PTR_DEMANGLE (fptr); |
171689da FW |
345 | return fptr; |
346 | } | |
347 | ||
f8847d83 DD |
348 | #if defined SHARED && defined USE_NSCD |
349 | /* Load all libraries for the service. */ | |
350 | static void | |
9b456c5d | 351 | nss_load_all_libraries (enum nss_database service) |
f8847d83 DD |
352 | { |
353 | nss_action_list ni = NULL; | |
354 | ||
9b456c5d | 355 | if (__nss_database_get (service, &ni)) |
f8847d83 DD |
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; | |
f8847d83 | 375 | PTR_MANGLE (cb); |
f8847d83 DD |
376 | nscd_init_cb = cb; |
377 | is_nscd = true; | |
378 | ||
379 | /* Find all the relevant modules so that the init functions are called. */ | |
9b456c5d DD |
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); | |
f8847d83 DD |
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 | ||
429029a7 DD |
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 | ||
88677348 | 419 | void |
171689da FW |
420 | __nss_module_freeres (void) |
421 | { | |
422 | struct nss_module *current = nss_module_list; | |
423 | while (current != NULL) | |
424 | { | |
ee5ed999 FW |
425 | /* Ignore built-in modules (which have a NULL handle). */ |
426 | if (current->state == nss_module_loaded | |
427 | && current->handle != NULL) | |
171689da FW |
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 | } |