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.
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 "nss_database.h"
20
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>
31
32struct nss_database_state
33{
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;
41};
42
43
44/* Global NSS database state. Underlying type is "struct
45 nss_database_state *" but the allocate_once API requires
46 "void *". */
47static void *global_database_state;
48
49/* Allocate and return pointer to nss_database_state object or
50 on failure return NULL. */
51static void *
52global_state_allocate (void *closure)
53{
54 struct nss_database_state *result = malloc (size: sizeof (*result));
55 if (result != NULL)
56 {
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;
64 }
65 return result;
66}
67
68/* Return pointer to global NSS database state, allocating as
69 required, or returning NULL on failure. */
70static struct nss_database_state *
71nss_database_state_get (void)
72{
73 return allocate_once (place: &global_database_state, allocate: global_state_allocate,
74 NULL, NULL);
75}
76
77/* Database default selections. nis/compat mappings get turned into
78 "files" for !LINK_OBSOLETE_NSL configurations. */
79enum nss_database_default
80{
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. */
88
89 NSS_DATABASE_DEFAULT_COUNT /* Number of defaults. */
90};
91
92/* Databases not listed default to nss_database_default_defconfig. */
93static const char per_database_defaults[NSS_DATABASE_COUNT] =
94 {
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,
106 };
107
108struct nss_database_default_cache
109{
110 nss_action_list caches[NSS_DATABASE_DEFAULT_COUNT];
111};
112
113static bool
114nss_database_select_default (struct nss_database_default_cache *cache,
115 enum nss_database db, nss_action_list *result)
116{
117 enum nss_database_default def = per_database_defaults[db];
118 *result = cache->caches[def];
119 if (*result != NULL)
120 return true;
121
122 /* Determine the default line string. */
123 const char *line;
124 switch (def)
125 {
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
134
135 case nss_database_default_dns:
136 line = "files dns";
137 break;
138
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;
146
147 case nss_database_default_nis:
148 line = "nis";
149 break;
150
151 case nss_database_default_nis_nisplus:
152 line = "nis nisplus";
153 break;
154
155 case nss_database_default_none:
156 /* Very special case: Leave *result as NULL. */
157 return true;
158
159 case NSS_DATABASE_DEFAULT_COUNT:
160 __builtin_unreachable ();
161 }
162 if (def < 0 || def >= NSS_DATABASE_DEFAULT_COUNT)
163 /* Tell GCC that line is initialized. */
164 __builtin_unreachable ();
165
166 *result = __nss_action_parse (line);
167 if (*result == NULL)
168 {
169 assert (errno == ENOMEM);
170 return false;
171 }
172 return true;
173}
174
175/* database_name must be large enough for each individual name plus a
176 null terminator. */
177typedef 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
182
183static const database_name nss_database_name_array[] =
184 {
185#define DEFINE_DATABASE(name) #name,
186#include "databases.def"
187#undef DEFINE_DATABASE
188 };
189
190static int
191name_search (const void *left, const void *right)
192{
193 return strcmp (left, right);
194}
195
196static int
197name_to_database_index (const char *name)
198{
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;
205}
206
207static bool
208process_line (struct nss_database_data *data, char *line)
209{
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;
216
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';
226
227 int db = name_to_database_index (name);
228 if (db < 0)
229 /* Not our database e.g. sudoers, automount, etc. */
230 return true;
231
232 nss_action_list result = __nss_action_parse (line);
233 if (result == NULL)
234 return false;
235 data->services[db] = result;
236 return true;
237}
238
239int
240__nss_configure_lookup (const char *dbname, const char *service_line)
241{
242 int db;
243 nss_action_list result;
244 struct nss_database_state *local;
245
246 /* Convert named database to index. */
247 db = name_to_database_index (name: dbname);
248 if (db < 0)
249 /* Not our database (e.g., sudoers). */
250 return -1;
251
252 /* Force any load/cache/read whatever to happen, so we can override
253 it. */
254 __nss_database_get (db, &result);
255
256 local = nss_database_state_get ();
257
258 result = __nss_action_parse (line: service_line);
259 if (result == NULL)
260 return -1;
261
262 atomic_store_release (&local->data.reload_disabled, 1);
263 local->data.services[db] = result;
264
265#ifdef USE_NSCD
266 __nss_database_custom[db] = true;
267#endif
268
269 return 0;
270}
271
272/* Iterate over the lines in FP, parse them, and store them in DATA.
273 Return false on memory allocation failure, true on success. */
274static bool
275nss_database_reload_1 (struct nss_database_data *data, FILE *fp)
276{
277 char *line = NULL;
278 size_t line_allocated = 0;
279 bool result = false;
280
281 while (true)
282 {
283 ssize_t ret = __getline (lineptr: &line, n: &line_allocated, stream: fp);
284 if (__ferror_unlocked (stream: fp))
285 break;
286 if (__feof_unlocked (stream: fp))
287 {
288 result = true;
289 break;
290 }
291 assert (ret > 0);
292 (void) ret; /* For NDEBUG builds. */
293
294 if (!process_line (data, line))
295 break;
296 }
297
298 free (ptr: line);
299 return result;
300}
301
302static bool
303nss_database_reload (struct nss_database_data *staging,
304 struct file_change_detection *initial)
305{
306 FILE *fp = fopen (_PATH_NSSWITCH_CONF, "rce");
307 if (fp == NULL)
308 switch (errno)
309 {
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;
323 }
324 else
325 /* No other threads have access to fp. */
326 __fsetlocking (fp, FSETLOCKING_BYCALLER);
327
328 /* We start with all of *staging pointing to NULL. */
329
330 bool ok = true;
331 if (fp != NULL)
332 ok = nss_database_reload_1 (data: staging, fp);
333
334 /* Now we have non-NULL entries where the user explictly listed the
335 service in nsswitch.conf. */
336
337 /* Apply defaults. */
338 if (ok)
339 {
340 struct nss_database_default_cache cache = { };
341
342 /* These three default to other services if the user listed the
343 other service. */
344
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];
350
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];
356
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];
362
363 /* For anything still unspecified, load the default configs. */
364
365 for (int i = 0; i < NSS_DATABASE_COUNT; ++i)
366 if (staging->services[i] == NULL)
367 {
368 ok = nss_database_select_default (cache: &cache, db: i,
369 result: &staging->services[i]);
370 if (!ok)
371 break;
372 }
373 }
374
375 if (ok)
376 ok = __file_change_detection_for_fp (&staging->nsswitch_conf, fp);
377
378 if (fp != NULL)
379 {
380 int saved_errno = errno;
381 fclose (fp);
382 __set_errno (saved_errno);
383 }
384
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;
388
389 return ok;
390}
391
392static bool
393nss_database_check_reload_and_get (struct nss_database_state *local,
394 nss_action_list *result,
395 enum nss_database database_index)
396{
397 struct __stat64_t64 str;
398
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))
403 {
404 *result = local->data.services[database_index];
405 /* No reload, so there is no error. */
406 return true;
407 }
408
409 struct file_change_detection initial;
410 if (!__file_change_detection_for_path (&initial, _PATH_NSSWITCH_CONF))
411 return false;
412
413 __libc_lock_lock (local->lock);
414 if (__file_is_unchanged (&initial, &local->data.nsswitch_conf))
415 {
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;
421 }
422
423 int stat_rv = __stat64_time64 ("/", &str);
424
425 if (local->data.services[database_index] != NULL)
426 {
427 /* Before we reload, verify that "/" hasn't changed. We assume that
428 errors here are very unlikely, but the chance that we're entering
429 a container is also very unlikely, so we err on the side of both
430 very unlikely things not happening at the same time. */
431 if (stat_rv != 0
432 || (local->root_ino != 0
433 && (str.st_ino != local->root_ino
434 || str.st_dev != local->root_dev)))
435 {
436 /* Change detected; disable reloading and return current state. */
437 atomic_store_release (&local->data.reload_disabled, 1);
438 *result = local->data.services[database_index];
439 __libc_lock_unlock (local->lock);
440 return true;
441 }
442 }
443 if (stat_rv == 0)
444 {
445 local->root_ino = str.st_ino;
446 local->root_dev = str.st_dev;
447 }
448
449 __libc_lock_unlock (local->lock);
450
451 /* Avoid overwriting the global configuration until we have loaded
452 everything successfully. Otherwise, if the file change
453 information changes back to what is in the global configuration,
454 the lookups would use the partially-written configuration. */
455 struct nss_database_data staging = { .initialized = true, };
456
457 bool ok = nss_database_reload (staging: &staging, initial: &initial);
458
459 if (ok)
460 {
461 __libc_lock_lock (local->lock);
462
463 /* See above for memory order. */
464 if (!atomic_load_acquire (&local->data.reload_disabled))
465 /* This may go back in time if another thread beats this
466 thread with the update, but in this case, a reload happens
467 on the next NSS call. */
468 local->data = staging;
469
470 *result = local->data.services[database_index];
471 __libc_lock_unlock (local->lock);
472 }
473
474 return ok;
475}
476
477bool
478__nss_database_get (enum nss_database db, nss_action_list *actions)
479{
480 struct nss_database_state *local = nss_database_state_get ();
481 return nss_database_check_reload_and_get (local, result: actions, database_index: db);
482}
483libc_hidden_def (__nss_database_get)
484
485nss_action_list
486__nss_database_get_noreload (enum nss_database db)
487{
488 /* There must have been a previous __nss_database_get call. */
489 struct nss_database_state *local = atomic_load_acquire (&global_database_state);
490 assert (local != NULL);
491
492 __libc_lock_lock (local->lock);
493 nss_action_list result = local->data.services[db];
494 __libc_lock_unlock (local->lock);
495 return result;
496}
497
498void __libc_freeres_fn_section
499__nss_database_freeres (void)
500{
501 free (ptr: global_database_state);
502 global_database_state = NULL;
503}
504
505void
506__nss_database_fork_prepare_parent (struct nss_database_data *data)
507{
508 /* Do not use allocate_once to trigger loading unnecessarily. */
509 struct nss_database_state *local = atomic_load_acquire (&global_database_state);
510 if (local == NULL)
511 data->initialized = false;
512 else
513 {
514 /* Make a copy of the configuration. This approach was chosen
515 because it avoids acquiring the lock during the actual
516 fork. */
517 __libc_lock_lock (local->lock);
518 *data = local->data;
519 __libc_lock_unlock (local->lock);
520 }
521}
522
523void
524__nss_database_fork_subprocess (struct nss_database_data *data)
525{
526 struct nss_database_state *local = atomic_load_acquire (&global_database_state);
527 if (data->initialized)
528 {
529 /* Restore the state at the point of the fork. */
530 assert (local != NULL);
531 local->data = *data;
532 __libc_lock_init (local->lock);
533 }
534 else if (local != NULL)
535 /* The NSS configuration was loaded concurrently during fork. We
536 do not know its state, so we need to discard it. */
537 global_database_state = NULL;
538}
539

source code of glibc/nss/nss_database.c