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>
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 explictly 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 /* 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
);
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
);
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
);
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
)
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
);
489 void __libc_freeres_fn_section
490 __nss_database_freeres (void)
492 free (global_database_state
);
493 global_database_state
= NULL
;
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
);
502 data
->initialized
= false;
505 /* Make a copy of the configuration. This approach was chosen
506 because it avoids acquiring the lock during the actual
508 __libc_lock_lock (local
->lock
);
510 __libc_lock_unlock (local
->lock
);
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
);
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
;