1 /* Mapping NSS services to action lists.
2 Copyright (C) 2020-2023 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>
26 #include <file_change_detection.h>
27 #include <libc-lock.h>
29 #include <stdio_ext.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
44 /* Global NSS database state. Underlying type is "struct
45 nss_database_state *" but the allocate_once API requires
47 static void *global_database_state
;
49 /* Allocate and return pointer to nss_database_state object or
50 on failure return NULL. */
52 global_state_allocate (void *closure
)
54 struct nss_database_state
*result
= malloc (sizeof (*result
));
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
);
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
,
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
];
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
];
122 /* Determine the default line string. */
126 #ifdef LINK_OBSOLETE_NSL
127 case nss_database_default_defconfig
:
128 line
= "nis [NOTFOUND=return] files";
130 case nss_database_default_compat
:
131 line
= "compat [NOTFOUND=return] files";
135 case nss_database_default_dns
:
139 case nss_database_default_files
:
140 #ifndef LINK_OBSOLETE_NSL
141 case nss_database_default_defconfig
:
142 case nss_database_default_compat
:
147 case nss_database_default_nis
:
151 case nss_database_default_nis_nisplus
:
152 line
= "nis nisplus";
155 case nss_database_default_none
:
156 /* Very special case: Leave *result as NULL. */
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
);
169 assert (errno
== ENOMEM
);
175 /* database_name must be large enough for each individual name plus a
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
191 name_search (const void *left
, const void *right
)
193 return strcmp (left
, right
);
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
)
204 return name_entry
- nss_database_name_array
;
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]))
217 /* Recognize `<database> ":"'. */
219 while (line
[0] != '\0' && !isspace (line
[0]) && line
[0] != ':')
221 if (line
[0] == '\0' || name
== line
)
222 /* Syntax error. Skip this line. */
224 while (line
[0] != '\0' && (isspace (line
[0]) || line
[0] == ':'))
227 int db
= name_to_database_index (name
);
229 /* Not our database e.g. sudoers, automount, etc. */
232 nss_action_list result
= __nss_action_parse (line
);
235 data
->services
[db
] = result
;
240 __nss_configure_lookup (const char *dbname
, const char *service_line
)
243 nss_action_list result
;
244 struct nss_database_state
*local
;
246 /* Convert named database to index. */
247 db
= name_to_database_index (dbname
);
249 /* Not our database (e.g., sudoers). */
252 /* Force any load/cache/read whatever to happen, so we can override
254 __nss_database_get (db
, &result
);
256 local
= nss_database_state_get ();
258 result
= __nss_action_parse (service_line
);
262 atomic_store_release (&local
->data
.reload_disabled
, 1);
263 local
->data
.services
[db
] = result
;
266 __nss_database_custom
[db
] = true;
272 /* Iterate over the lines in FP, parse them, and store them in DATA.
273 Return false on memory allocation failure, true on success. */
275 nss_database_reload_1 (struct nss_database_data
*data
, FILE *fp
)
278 size_t line_allocated
= 0;
283 ssize_t ret
= __getline (&line
, &line_allocated
, fp
);
284 if (__ferror_unlocked (fp
))
286 if (__feof_unlocked (fp
))
292 (void) ret
; /* For NDEBUG builds. */
294 if (!process_line (data
, line
))
303 nss_database_reload (struct nss_database_data
*staging
,
304 struct file_change_detection
*initial
)
306 FILE *fp
= fopen (_PATH_NSSWITCH_CONF
, "rce");
316 /* Ignore these errors. They are persistent errors caused
317 by file system contents. */
320 /* Other errors refer to resource allocation problems and
321 need to be handled by the application. */
325 /* No other threads have access to fp. */
326 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
328 /* We start with all of *staging pointing to NULL. */
332 ok
= nss_database_reload_1 (staging
, fp
);
334 /* Now we have non-NULL entries where the user explicitly listed the
335 service in nsswitch.conf. */
337 /* Apply defaults. */
340 struct nss_database_default_cache cache
= { };
342 /* These three default to other services if the user listed the
345 /* "shadow_compat" defaults to "passwd_compat" if only the
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
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
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
]);
376 ok
= __file_change_detection_for_fp (&staging
->nsswitch_conf
, fp
);
380 int saved_errno
= errno
;
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;
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. */
409 struct file_change_detection initial
;
410 if (!__file_change_detection_for_path (&initial
, _PATH_NSSWITCH_CONF
))
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
418 *result
= local
->data
.services
[database_index
];
419 __libc_lock_unlock (local
->lock
);
423 int stat_rv
= __stat64_time64 ("/", &str
);
425 if (local
->data
.services
[database_index
] != NULL
)
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. */
432 || (local
->root_ino
!= 0
433 && (str
.st_ino
!= local
->root_ino
434 || str
.st_dev
!= local
->root_dev
)))
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
);
445 local
->root_ino
= str
.st_ino
;
446 local
->root_dev
= str
.st_dev
;
449 __libc_lock_unlock (local
->lock
);
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, };
457 bool ok
= nss_database_reload (&staging
, &initial
);
461 __libc_lock_lock (local
->lock
);
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
;
470 *result
= local
->data
.services
[database_index
];
471 __libc_lock_unlock (local
->lock
);
478 __nss_database_get (enum nss_database db
, nss_action_list
*actions
)
480 struct nss_database_state
*local
= nss_database_state_get ();
481 return nss_database_check_reload_and_get (local
, actions
, db
);
483 libc_hidden_def (__nss_database_get
)
486 __nss_database_get_noreload (enum nss_database db
)
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
);
492 __libc_lock_lock (local
->lock
);
493 nss_action_list result
= local
->data
.services
[db
];
494 __libc_lock_unlock (local
->lock
);
499 __nss_database_freeres (void)
501 free (global_database_state
);
502 global_database_state
= NULL
;
506 __nss_database_fork_prepare_parent (struct nss_database_data
*data
)
508 /* Do not use allocate_once to trigger loading unnecessarily. */
509 struct nss_database_state
*local
= atomic_load_acquire (&global_database_state
);
511 data
->initialized
= false;
514 /* Make a copy of the configuration. This approach was chosen
515 because it avoids acquiring the lock during the actual
517 __libc_lock_lock (local
->lock
);
519 __libc_lock_unlock (local
->lock
);
524 __nss_database_fork_subprocess (struct nss_database_data
*data
)
526 struct nss_database_state
*local
= atomic_load_acquire (&global_database_state
);
527 if (data
->initialized
)
529 /* Restore the state at the point of the fork. */
530 assert (local
!= NULL
);
532 __libc_lock_init (local
->lock
);
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
;