benchtests: Only build libmvec benchmarks iff $(build-mathvec) is set
[glibc.git] / nss / nss_database.c
blobd56c5b798da76a80b5d45fbbdf6871e11e57e62b
1 /* Mapping NSS services to action lists.
2 Copyright (C) 2020-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 #include "nss_database.h"
21 #include <allocate_once.h>
22 #include <array_length.h>
23 #include <assert.h>
24 #include <atomic.h>
25 #include <ctype.h>
26 #include <file_change_detection.h>
27 #include <libc-lock.h>
28 #include <netdb.h>
29 #include <stdio_ext.h>
30 #include <string.h>
32 struct nss_database_state
34 struct nss_database_data data;
35 __libc_lock_define (, lock);
36 /* If "/" changes, we switched into a container and do NOT want to
37 reload anything. This data must be persistent across
38 reloads. */
39 ino64_t root_ino;
40 dev_t root_dev;
44 /* Global NSS database state. Underlying type is "struct
45 nss_database_state *" but the allocate_once API requires
46 "void *". */
47 static void *global_database_state;
49 /* Allocate and return pointer to nss_database_state object or
50 on failure return NULL. */
51 static void *
52 global_state_allocate (void *closure)
54 struct nss_database_state *result = malloc (sizeof (*result));
55 if (result != NULL)
57 result->data.nsswitch_conf.size = -1; /* Force reload. */
58 memset (result->data.services, 0, sizeof (result->data.services));
59 result->data.initialized = true;
60 result->data.reload_disabled = false;
61 __libc_lock_init (result->lock);
62 result->root_ino = 0;
63 result->root_dev = 0;
65 return result;
68 /* Return pointer to global NSS database state, allocating as
69 required, or returning NULL on failure. */
70 static struct nss_database_state *
71 nss_database_state_get (void)
73 return allocate_once (&global_database_state, global_state_allocate,
74 NULL, NULL);
77 /* Database default selections. nis/compat mappings get turned into
78 "files" for !LINK_OBSOLETE_NSL configurations. */
79 enum nss_database_default
81 nss_database_default_defconfig = 0, /* "nis [NOTFOUND=return] files". */
82 nss_database_default_compat, /* "compat [NOTFOUND=return] files". */
83 nss_database_default_dns, /* "files dns". */
84 nss_database_default_files, /* "files". */
85 nss_database_default_nis, /* "nis". */
86 nss_database_default_nis_nisplus, /* "nis nisplus". */
87 nss_database_default_none, /* Empty list. */
89 NSS_DATABASE_DEFAULT_COUNT /* Number of defaults. */
92 /* Databases not listed default to nss_database_default_defconfig. */
93 static const char per_database_defaults[NSS_DATABASE_COUNT] =
95 [nss_database_group] = nss_database_default_compat,
96 [nss_database_group_compat] = nss_database_default_nis,
97 [nss_database_gshadow] = nss_database_default_files,
98 [nss_database_hosts] = nss_database_default_dns,
99 [nss_database_initgroups] = nss_database_default_none,
100 [nss_database_networks] = nss_database_default_dns,
101 [nss_database_passwd] = nss_database_default_compat,
102 [nss_database_passwd_compat] = nss_database_default_nis,
103 [nss_database_publickey] = nss_database_default_nis_nisplus,
104 [nss_database_shadow] = nss_database_default_compat,
105 [nss_database_shadow_compat] = nss_database_default_nis,
108 struct nss_database_default_cache
110 nss_action_list caches[NSS_DATABASE_DEFAULT_COUNT];
113 static bool
114 nss_database_select_default (struct nss_database_default_cache *cache,
115 enum nss_database db, nss_action_list *result)
117 enum nss_database_default def = per_database_defaults[db];
118 *result = cache->caches[def];
119 if (*result != NULL)
120 return true;
122 /* Determine the default line string. */
123 const char *line;
124 switch (def)
126 #ifdef LINK_OBSOLETE_NSL
127 case nss_database_default_defconfig:
128 line = "nis [NOTFOUND=return] files";
129 break;
130 case nss_database_default_compat:
131 line = "compat [NOTFOUND=return] files";
132 break;
133 #endif
135 case nss_database_default_dns:
136 line = "files dns";
137 break;
139 case nss_database_default_files:
140 #ifndef LINK_OBSOLETE_NSL
141 case nss_database_default_defconfig:
142 case nss_database_default_compat:
143 #endif
144 line = "files";
145 break;
147 case nss_database_default_nis:
148 line = "nis";
149 break;
151 case nss_database_default_nis_nisplus:
152 line = "nis nisplus";
153 break;
155 case nss_database_default_none:
156 /* Very special case: Leave *result as NULL. */
157 return true;
159 case NSS_DATABASE_DEFAULT_COUNT:
160 __builtin_unreachable ();
162 if (def < 0 || def >= NSS_DATABASE_DEFAULT_COUNT)
163 /* Tell GCC that line is initialized. */
164 __builtin_unreachable ();
166 *result = __nss_action_parse (line);
167 if (*result == NULL)
169 assert (errno == ENOMEM);
170 return false;
172 return true;
175 /* database_name must be large enough for each individual name plus a
176 null terminator. */
177 typedef char database_name[14];
178 #define DEFINE_DATABASE(name) \
179 _Static_assert (sizeof (#name) <= sizeof (database_name), #name);
180 #include "databases.def"
181 #undef DEFINE_DATABASE
183 static const database_name nss_database_name_array[] =
185 #define DEFINE_DATABASE(name) #name,
186 #include "databases.def"
187 #undef DEFINE_DATABASE
190 static int
191 name_search (const void *left, const void *right)
193 return strcmp (left, right);
196 static int
197 name_to_database_index (const char *name)
199 database_name *name_entry = bsearch (name, nss_database_name_array,
200 array_length (nss_database_name_array),
201 sizeof (database_name), name_search);
202 if (name_entry == NULL)
203 return -1;
204 return name_entry - nss_database_name_array;
207 static bool
208 process_line (struct nss_database_data *data, char *line)
210 /* Ignore leading white spaces. ATTENTION: this is different from
211 what is implemented in Solaris. The Solaris man page says a line
212 beginning with a white space character is ignored. We regard
213 this as just another misfeature in Solaris. */
214 while (isspace (line[0]))
215 ++line;
217 /* Recognize `<database> ":"'. */
218 char *name = line;
219 while (line[0] != '\0' && !isspace (line[0]) && line[0] != ':')
220 ++line;
221 if (line[0] == '\0' || name == line)
222 /* Syntax error. Skip this line. */
223 return true;
224 while (line[0] != '\0' && (isspace (line[0]) || line[0] == ':'))
225 *line++ = '\0';
227 int db = name_to_database_index (name);
228 if (db < 0)
229 /* Not our database e.g. sudoers, automount, etc. */
230 return true;
232 nss_action_list result = __nss_action_parse (line);
233 if (result == NULL)
234 return false;
235 data->services[db] = result;
236 return true;
240 __nss_configure_lookup (const char *dbname, const char *service_line)
242 int db;
243 nss_action_list result;
244 struct nss_database_state *local;
246 /* Convert named database to index. */
247 db = name_to_database_index (dbname);
248 if (db < 0)
249 /* Not our database (e.g., sudoers). */
250 return -1;
252 /* Force any load/cache/read whatever to happen, so we can override
253 it. */
254 __nss_database_get (db, &result);
256 local = nss_database_state_get ();
258 result = __nss_action_parse (service_line);
259 if (result == NULL)
260 return -1;
262 atomic_store_release (&local->data.reload_disabled, 1);
263 local->data.services[db] = result;
265 #ifdef USE_NSCD
266 __nss_database_custom[db] = true;
267 #endif
269 return 0;
272 /* Iterate over the lines in FP, parse them, and store them in DATA.
273 Return false on memory allocation failure, true on success. */
274 static bool
275 nss_database_reload_1 (struct nss_database_data *data, FILE *fp)
277 char *line = NULL;
278 size_t line_allocated = 0;
279 bool result = false;
281 while (true)
283 ssize_t ret = __getline (&line, &line_allocated, fp);
284 if (__ferror_unlocked (fp))
285 break;
286 if (__feof_unlocked (fp))
288 result = true;
289 break;
291 assert (ret > 0);
292 (void) ret; /* For NDEBUG builds. */
294 if (!process_line (data, line))
295 break;
298 free (line);
299 return result;
302 static bool
303 nss_database_reload (struct nss_database_data *staging,
304 struct file_change_detection *initial)
306 FILE *fp = fopen (_PATH_NSSWITCH_CONF, "rce");
307 if (fp == NULL)
308 switch (errno)
310 case EACCES:
311 case EISDIR:
312 case ELOOP:
313 case ENOENT:
314 case ENOTDIR:
315 case EPERM:
316 /* Ignore these errors. They are persistent errors caused
317 by file system contents. */
318 break;
319 default:
320 /* Other errors refer to resource allocation problems and
321 need to be handled by the application. */
322 return false;
324 else
325 /* No other threads have access to fp. */
326 __fsetlocking (fp, FSETLOCKING_BYCALLER);
328 /* We start with all of *staging pointing to NULL. */
330 bool ok = true;
331 if (fp != NULL)
332 ok = nss_database_reload_1 (staging, fp);
334 /* Now we have non-NULL entries where the user explictly listed the
335 service in nsswitch.conf. */
337 /* Apply defaults. */
338 if (ok)
340 struct nss_database_default_cache cache = { };
342 /* These three default to other services if the user listed the
343 other service. */
345 /* "shadow_compat" defaults to "passwd_compat" if only the
346 latter is given. */
347 if (staging->services[nss_database_shadow_compat] == NULL)
348 staging->services[nss_database_shadow_compat] =
349 staging->services[nss_database_passwd_compat];
351 /* "shadow" defaults to "passwd" if only the latter is
352 given. */
353 if (staging->services[nss_database_shadow] == NULL)
354 staging->services[nss_database_shadow] =
355 staging->services[nss_database_passwd];
357 /* "gshadow" defaults to "group" if only the latter is
358 given. */
359 if (staging->services[nss_database_gshadow] == NULL)
360 staging->services[nss_database_gshadow] =
361 staging->services[nss_database_group];
363 /* For anything still unspecified, load the default configs. */
365 for (int i = 0; i < NSS_DATABASE_COUNT; ++i)
366 if (staging->services[i] == NULL)
368 ok = nss_database_select_default (&cache, i,
369 &staging->services[i]);
370 if (!ok)
371 break;
375 if (ok)
376 ok = __file_change_detection_for_fp (&staging->nsswitch_conf, fp);
378 if (fp != NULL)
380 int saved_errno = errno;
381 fclose (fp);
382 __set_errno (saved_errno);
385 if (ok && !__file_is_unchanged (&staging->nsswitch_conf, initial))
386 /* Reload is required because the file changed while reading. */
387 staging->nsswitch_conf.size = -1;
389 return ok;
392 static bool
393 nss_database_check_reload_and_get (struct nss_database_state *local,
394 nss_action_list *result,
395 enum nss_database database_index)
397 struct __stat64_t64 str;
399 /* Acquire MO is needed because the thread that sets reload_disabled
400 may have loaded the configuration first, so synchronize with the
401 Release MO store there. */
402 if (atomic_load_acquire (&local->data.reload_disabled))
404 *result = local->data.services[database_index];
405 /* No reload, so there is no error. */
406 return true;
409 struct file_change_detection initial;
410 if (!__file_change_detection_for_path (&initial, _PATH_NSSWITCH_CONF))
411 return false;
413 __libc_lock_lock (local->lock);
414 if (__file_is_unchanged (&initial, &local->data.nsswitch_conf))
416 /* Configuration is up-to-date. Read it and return it to the
417 caller. */
418 *result = local->data.services[database_index];
419 __libc_lock_unlock (local->lock);
420 return true;
423 /* Before we reload, verify that "/" hasn't changed. We assume that
424 errors here are very unlikely, but the chance that we're entering
425 a container is also very unlikely, so we err on the side of both
426 very unlikely things not happening at the same time. */
427 if (__stat64_time64 ("/", &str) != 0
428 || (local->root_ino != 0
429 && (str.st_ino != local->root_ino
430 || str.st_dev != local->root_dev)))
432 /* Change detected; disable reloading and return current state. */
433 atomic_store_release (&local->data.reload_disabled, 1);
434 *result = local->data.services[database_index];
435 __libc_lock_unlock (local->lock);
436 return true;
438 local->root_ino = str.st_ino;
439 local->root_dev = str.st_dev;
440 __libc_lock_unlock (local->lock);
442 /* Avoid overwriting the global configuration until we have loaded
443 everything successfully. Otherwise, if the file change
444 information changes back to what is in the global configuration,
445 the lookups would use the partially-written configuration. */
446 struct nss_database_data staging = { .initialized = true, };
448 bool ok = nss_database_reload (&staging, &initial);
450 if (ok)
452 __libc_lock_lock (local->lock);
454 /* See above for memory order. */
455 if (!atomic_load_acquire (&local->data.reload_disabled))
456 /* This may go back in time if another thread beats this
457 thread with the update, but in this case, a reload happens
458 on the next NSS call. */
459 local->data = staging;
461 *result = local->data.services[database_index];
462 __libc_lock_unlock (local->lock);
465 return ok;
468 bool
469 __nss_database_get (enum nss_database db, nss_action_list *actions)
471 struct nss_database_state *local = nss_database_state_get ();
472 return nss_database_check_reload_and_get (local, actions, db);
474 libc_hidden_def (__nss_database_get)
476 nss_action_list
477 __nss_database_get_noreload (enum nss_database db)
479 /* There must have been a previous __nss_database_get call. */
480 struct nss_database_state *local = atomic_load_acquire (&global_database_state);
481 assert (local != NULL);
483 __libc_lock_lock (local->lock);
484 nss_action_list result = local->data.services[db];
485 __libc_lock_unlock (local->lock);
486 return result;
489 void __libc_freeres_fn_section
490 __nss_database_freeres (void)
492 free (global_database_state);
493 global_database_state = NULL;
496 void
497 __nss_database_fork_prepare_parent (struct nss_database_data *data)
499 /* Do not use allocate_once to trigger loading unnecessarily. */
500 struct nss_database_state *local = atomic_load_acquire (&global_database_state);
501 if (local == NULL)
502 data->initialized = false;
503 else
505 /* Make a copy of the configuration. This approach was chosen
506 because it avoids acquiring the lock during the actual
507 fork. */
508 __libc_lock_lock (local->lock);
509 *data = local->data;
510 __libc_lock_unlock (local->lock);
514 void
515 __nss_database_fork_subprocess (struct nss_database_data *data)
517 struct nss_database_state *local = atomic_load_acquire (&global_database_state);
518 if (data->initialized)
520 /* Restore the state at the point of the fork. */
521 assert (local != NULL);
522 local->data = *data;
523 __libc_lock_init (local->lock);
525 else if (local != NULL)
526 /* The NSS configuration was loaded concurrently during fork. We
527 do not know its state, so we need to discard it. */
528 global_database_state = NULL;