]> git.ipfire.org Git - thirdparty/glibc.git/blob - nss/nss_module.c
fdb4e994df9f880c74af351726153a33fa11e356
[thirdparty/glibc.git] / nss / nss_module.c
1 /* Global list of NSS service modules.
2 Copyright (c) 2020 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 <stddef.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #ifdef LINK_OBSOLETE_NSL
35 # define DEFAULT_CONFIG "compat [NOTFOUND=return] files"
36 # define DEFAULT_DEFCONFIG "nis [NOTFOUND=return] files"
37 #else
38 # define DEFAULT_CONFIG "files"
39 # define DEFAULT_DEFCONFIG "files"
40 #endif
41
42 /* Suffix after .so of NSS service modules. This is a bit of magic,
43 but we assume LIBNSS_FILES_SO looks like "libnss_files.so.2" and we
44 want a pointer to the ".2" part. We have no API to extract this
45 except through the auto-generated lib-names.h and some static
46 pointer manipulation. The "-1" accounts for the trailing NUL
47 included in the sizeof. */
48 static const char *const __nss_shlib_revision
49 = LIBNSS_FILES_SO + sizeof("libnss_files.so") - 1;
50
51 /* A single-linked list used to implement a mapping from service names
52 to NSS modules. (Most systems only use five or so modules, so a
53 list is sufficient here.) Elements of this list are never freed
54 during normal operation. */
55 static struct nss_module *nss_module_list;
56
57 /* Covers the list and also loading of individual NSS service
58 modules. */
59 __libc_lock_define (static, nss_module_list_lock);
60
61 #if defined USE_NSCD && (!defined DO_STATIC_NSS || defined SHARED)
62 /* Nonzero if this is the nscd process. */
63 static bool is_nscd;
64 /* The callback passed to the init functions when nscd is used. */
65 static void (*nscd_init_cb) (size_t, struct traced_file *);
66 #endif
67
68 /* Allocate the service NAME with length NAME_LENGTH. If the service
69 is already allocated in the nss_module_list cache then we return a
70 pointer to the struct nss_module, otherwise we try to allocate a
71 new struct nss_module entry and add it to the global
72 nss_modules_list cache. If we fail to allocate the entry we return
73 NULL. Failure to allocate the entry is always transient. */
74 struct nss_module *
75 __nss_module_allocate (const char *name, size_t name_length)
76 {
77 __libc_lock_lock (nss_module_list_lock);
78
79 struct nss_module *result = NULL;
80 for (struct nss_module *p = nss_module_list; p != NULL; p = p->next)
81 if (strncmp (p->name, name, name_length) == 0
82 && p->name[name_length] == '\0')
83 {
84 /* Return the previously existing object. */
85 result = p;
86 break;
87 }
88
89 if (result == NULL)
90 {
91 /* Allocate a new list entry if the name was not found in the
92 list. */
93 result = malloc (sizeof (*result) + name_length + 1);
94 if (result != NULL)
95 {
96 result->state = nss_module_uninitialized;
97 memcpy (result->name, name, name_length);
98 result->name[name_length] = '\0';
99 result->handle = NULL;
100 result->next = nss_module_list;
101 nss_module_list = result;
102 }
103 }
104
105 __libc_lock_unlock (nss_module_list_lock);
106 return result;
107 }
108
109 /* Long enough to store the name of any function in the
110 nss_function_name_array list below, as getprotobynumber_r is the
111 longest entry in that list. */
112 typedef char function_name[sizeof("getprotobynumber_r")];
113
114 static const function_name nss_function_name_array[] =
115 {
116 #undef DEFINE_NSS_FUNCTION
117 #define DEFINE_NSS_FUNCTION(x) #x,
118 #include "function.def"
119 };
120
121 /* Internal implementation of __nss_module_load. */
122 static bool
123 module_load (struct nss_module *module)
124 {
125 void *handle;
126 {
127 char *shlib_name;
128 if (__asprintf (&shlib_name, "libnss_%s.so%s",
129 module->name, __nss_shlib_revision) < 0)
130 /* This is definitely a temporary failure. Do not update
131 module->state. This will trigger another attempt at the next
132 call. */
133 return false;
134
135 handle = __libc_dlopen (shlib_name);
136 free (shlib_name);
137 }
138
139 /* Failing to load the module can be caused by several different
140 scenarios. One such scenario is that the module has been removed
141 from the disk. In which case the in-memory version is all that
142 we have, and if the module->state indidates it is loaded then we
143 can use it. */
144 if (handle == NULL)
145 {
146 /* dlopen failure. We do not know if this a temporary or
147 permanent error. See bug 22041. Update the state using the
148 double-checked locking idiom. */
149
150 __libc_lock_lock (nss_module_list_lock);
151 bool result = result;
152 switch ((enum nss_module_state) atomic_load_acquire (&module->state))
153 {
154 case nss_module_uninitialized:
155 atomic_store_release (&module->state, nss_module_failed);
156 result = false;
157 break;
158 case nss_module_loaded:
159 result = true;
160 break;
161 case nss_module_failed:
162 result = false;
163 break;
164 }
165 __libc_lock_unlock (nss_module_list_lock);
166 return result;
167 }
168
169 nss_module_functions_untyped pointers;
170
171 /* Look up and store locally all the function pointers we may need
172 later. Doing this now means the data will not change in the
173 future. */
174 for (size_t idx = 0; idx < array_length (nss_function_name_array); ++idx)
175 {
176 char *function_name;
177 if (__asprintf (&function_name, "_nss_%s_%s",
178 module->name, nss_function_name_array[idx]) < 0)
179 {
180 /* Definitely a temporary error. */
181 __libc_dlclose (handle);
182 return false;
183 }
184 pointers[idx] = __libc_dlsym (handle, function_name);
185 free (function_name);
186 #ifdef PTR_MANGLE
187 PTR_MANGLE (pointers[idx]);
188 #endif
189 }
190
191 # ifdef USE_NSCD
192 if (is_nscd)
193 {
194 /* Call the init function when nscd is used. */
195 size_t initlen = (5 + strlen (module->name)
196 + strlen ("_init") + 1);
197 char init_name[initlen];
198
199 /* Construct the init function name. */
200 __stpcpy (__stpcpy (__stpcpy (init_name,
201 "_nss_"),
202 module->name),
203 "_init");
204
205 /* Find the optional init function. */
206 void (*ifct) (void (*) (size_t, struct traced_file *))
207 = __libc_dlsym (handle, init_name);
208 if (ifct != NULL)
209 {
210 void (*cb) (size_t, struct traced_file *) = nscd_init_cb;
211 # ifdef PTR_DEMANGLE
212 PTR_DEMANGLE (cb);
213 # endif
214 ifct (cb);
215 }
216 }
217 # endif
218
219 /* Install the function pointers, following the double-checked
220 locking idiom. Delay this after all processing, in case loading
221 the module triggers unwinding. */
222 __libc_lock_lock (nss_module_list_lock);
223 switch ((enum nss_module_state) atomic_load_acquire (&module->state))
224 {
225 case nss_module_uninitialized:
226 case nss_module_failed:
227 memcpy (module->functions.untyped, pointers,
228 sizeof (module->functions.untyped));
229 module->handle = handle;
230 /* Synchronizes with unlocked __nss_module_load atomic_load_acquire. */
231 atomic_store_release (&module->state, nss_module_loaded);
232 break;
233 case nss_module_loaded:
234 /* If the module was already loaded, close our own handle. This
235 does not actually unload the modules, only the reference
236 counter is decremented for the loaded module. */
237 __libc_dlclose (handle);
238 break;
239 }
240 __libc_lock_unlock (nss_module_list_lock);
241 return true;
242 }
243
244 /* Force the module identified by MODULE to be loaded. We return
245 false if the module could not be loaded, true otherwise. Loading
246 the module requires looking up all the possible interface APIs and
247 caching the results. */
248 bool
249 __nss_module_load (struct nss_module *module)
250 {
251 switch ((enum nss_module_state) atomic_load_acquire (&module->state))
252 {
253 case nss_module_uninitialized:
254 return module_load (module);
255 case nss_module_loaded:
256 /* Loading has already succeeded. */
257 return true;
258 case nss_module_failed:
259 /* Loading previously failed. */
260 return false;
261 }
262 __builtin_unreachable ();
263 }
264
265 static int
266 name_search (const void *left, const void *right)
267 {
268 return strcmp (left, right);
269 }
270
271 /* Load module MODULE (if it isn't already) and return a pointer to
272 the module's implementation of NAME, otherwise return NULL on
273 failure or error. */
274 void *
275 __nss_module_get_function (struct nss_module *module, const char *name)
276 {
277 if (!__nss_module_load (module))
278 return NULL;
279
280 function_name *name_entry = bsearch (name, nss_function_name_array,
281 array_length (nss_function_name_array),
282 sizeof (function_name), name_search);
283 assert (name_entry != NULL);
284 size_t idx = name_entry - nss_function_name_array;
285 void *fptr = module->functions.untyped[idx];
286 #ifdef PTR_DEMANGLE
287 PTR_DEMANGLE (fptr);
288 #endif
289 return fptr;
290 }
291
292 #if defined SHARED && defined USE_NSCD
293 /* Load all libraries for the service. */
294 static void
295 nss_load_all_libraries (const char *service, const char *def)
296 {
297 nss_action_list ni = NULL;
298
299 if (__nss_database_lookup2 (service, NULL, def, &ni) == 0)
300 while (ni->module != NULL)
301 {
302 __nss_module_load (ni->module);
303 ++ni;
304 }
305 }
306
307 define_traced_file (pwd, _PATH_NSSWITCH_CONF);
308 define_traced_file (grp, _PATH_NSSWITCH_CONF);
309 define_traced_file (hst, _PATH_NSSWITCH_CONF);
310 define_traced_file (serv, _PATH_NSSWITCH_CONF);
311 define_traced_file (netgr, _PATH_NSSWITCH_CONF);
312
313 /* Called by nscd and nscd alone. */
314 void
315 __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
316 {
317 void (*cb1) (size_t, struct traced_file *);
318 cb1 = cb;
319 # ifdef PTR_MANGLE
320 PTR_MANGLE (cb);
321 # endif
322 nscd_init_cb = cb;
323 is_nscd = true;
324
325 /* Find all the relevant modules so that the init functions are called. */
326 nss_load_all_libraries ("passwd", DEFAULT_CONFIG);
327 nss_load_all_libraries ("group", DEFAULT_CONFIG);
328 nss_load_all_libraries ("hosts", "dns [!UNAVAIL=return] files");
329 nss_load_all_libraries ("services", NULL);
330
331 /* Make sure NSCD purges its cache if nsswitch.conf changes. */
332 init_traced_file (&pwd_traced_file.file, _PATH_NSSWITCH_CONF, 0);
333 cb1 (pwddb, &pwd_traced_file.file);
334 init_traced_file (&grp_traced_file.file, _PATH_NSSWITCH_CONF, 0);
335 cb1 (grpdb, &grp_traced_file.file);
336 init_traced_file (&hst_traced_file.file, _PATH_NSSWITCH_CONF, 0);
337 cb1 (hstdb, &hst_traced_file.file);
338 init_traced_file (&serv_traced_file.file, _PATH_NSSWITCH_CONF, 0);
339 cb1 (servdb, &serv_traced_file.file);
340 init_traced_file (&netgr_traced_file.file, _PATH_NSSWITCH_CONF, 0);
341 cb1 (netgrdb, &netgr_traced_file.file);
342
343 /* Disable all uses of NSCD. */
344 __nss_not_use_nscd_passwd = -1;
345 __nss_not_use_nscd_group = -1;
346 __nss_not_use_nscd_hosts = -1;
347 __nss_not_use_nscd_services = -1;
348 __nss_not_use_nscd_netgroup = -1;
349 }
350 #endif
351
352 void __libc_freeres_fn_section
353 __nss_module_freeres (void)
354 {
355 struct nss_module *current = nss_module_list;
356 while (current != NULL)
357 {
358 if (current->state == nss_module_loaded)
359 __libc_dlclose (current->handle);
360
361 struct nss_module *next = current->next;
362 free (current);
363 current = next;
364 }
365 nss_module_list = NULL;
366 }