1 /* Mapping NSS services to action lists.
2 Copyright (C) 2020 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
);
39 /* Global NSS database state. Underlying type is "struct
40 nss_database_state *" but the allocate_once API requires
42 static void *global_database_state
;
44 /* Allocate and return pointer to nss_database_state object or
45 on failure return NULL. */
47 global_state_allocate (void *closure
)
49 struct nss_database_state
*result
= malloc (sizeof (*result
));
52 result
->data
.nsswitch_conf
.size
= -1; /* Force reload. */
53 memset (result
->data
.services
, 0, sizeof (result
->data
.services
));
54 result
->data
.initialized
= true;
55 result
->data
.reload_disabled
= false;
56 __libc_lock_init (result
->lock
);
61 /* Return pointer to global NSS database state, allocating as
62 required, or returning NULL on failure. */
63 static struct nss_database_state
*
64 nss_database_state_get (void)
66 return allocate_once (&global_database_state
, global_state_allocate
,
70 /* Database default selections. nis/compat mappings get turned into
71 "files" for !LINK_OBSOLETE_NSL configurations. */
72 enum nss_database_default
74 nss_database_default_defconfig
= 0, /* "nis [NOTFOUND=return] files". */
75 nss_database_default_compat
, /* "compat [NOTFOUND=return] files". */
76 nss_database_default_dns
, /* "dns [!UNAVAIL=return] files". */
77 nss_database_default_files
, /* "files". */
78 nss_database_default_nis
, /* "nis". */
79 nss_database_default_nis_nisplus
, /* "nis nisplus". */
80 nss_database_default_none
, /* Empty list. */
82 NSS_DATABASE_DEFAULT_COUNT
/* Number of defaults. */
85 /* Databases not listed default to nss_database_default_defconfig. */
86 static const char per_database_defaults
[NSS_DATABASE_COUNT
] =
88 [nss_database_group
] = nss_database_default_compat
,
89 [nss_database_gshadow
] = nss_database_default_files
,
90 [nss_database_hosts
] = nss_database_default_dns
,
91 [nss_database_initgroups
] = nss_database_default_none
,
92 [nss_database_networks
] = nss_database_default_dns
,
93 [nss_database_passwd
] = nss_database_default_compat
,
94 [nss_database_publickey
] = nss_database_default_nis_nisplus
,
95 [nss_database_shadow
] = nss_database_default_compat
,
98 struct nss_database_default_cache
100 nss_action_list caches
[NSS_DATABASE_DEFAULT_COUNT
];
104 nss_database_select_default (struct nss_database_default_cache
*cache
,
105 enum nss_database db
, nss_action_list
*result
)
107 enum nss_database_default def
= per_database_defaults
[db
];
108 *result
= cache
->caches
[def
];
112 /* Determine the default line string. */
116 #ifdef LINK_OBSOLETE_NSL
117 case nss_database_default_defconfig
:
118 line
= "nis [NOTFOUND=return] files";
120 case nss_database_default_compat
:
121 line
= "compat [NOTFOUND=return] files";
125 case nss_database_default_dns
:
126 line
= "dns [!UNAVAIL=return] files";
129 case nss_database_default_files
:
130 #ifndef LINK_OBSOLETE_NSL
131 case nss_database_default_defconfig
:
132 case nss_database_default_compat
:
137 case nss_database_default_nis
:
141 case nss_database_default_nis_nisplus
:
142 line
= "nis nisplus";
145 case nss_database_default_none
:
146 /* Very special case: Leave *result as NULL. */
149 case NSS_DATABASE_DEFAULT_COUNT
:
150 __builtin_unreachable ();
152 if (def
< 0 || def
>= NSS_DATABASE_DEFAULT_COUNT
)
153 /* Tell GCC that line is initialized. */
154 __builtin_unreachable ();
156 *result
= __nss_action_parse (line
);
159 assert (errno
== ENOMEM
);
166 /* database_name must be large enough for each individual name plus a
168 typedef char database_name
[11];
169 #define DEFINE_DATABASE(name) \
170 _Static_assert (sizeof (#name) <= sizeof (database_name), #name);
171 #include "databases.def"
172 #undef DEFINE_DATABASE
174 static const database_name nss_database_name_array
[] =
176 #define DEFINE_DATABASE(name) #name,
177 #include "databases.def"
178 #undef DEFINE_DATABASE
182 name_search (const void *left
, const void *right
)
184 return strcmp (left
, right
);
188 name_to_database_index (const char *name
)
190 database_name
*name_entry
= bsearch (name
, nss_database_name_array
,
191 array_length (nss_database_name_array
),
192 sizeof (database_name
), name_search
);
193 if (name_entry
== NULL
)
195 return name_entry
- nss_database_name_array
;
199 process_line (struct nss_database_data
*data
, char *line
)
201 /* Ignore leading white spaces. ATTENTION: this is different from
202 what is implemented in Solaris. The Solaris man page says a line
203 beginning with a white space character is ignored. We regard
204 this as just another misfeature in Solaris. */
205 while (isspace (line
[0]))
208 /* Recognize `<database> ":"'. */
210 while (line
[0] != '\0' && !isspace (line
[0]) && line
[0] != ':')
212 if (line
[0] == '\0' || name
== line
)
213 /* Syntax error. Skip this line. */
217 int db
= name_to_database_index (name
);
219 /* Not our database e.g. sudoers, automount, etc. */
222 nss_action_list result
= __nss_action_parse (line
);
225 data
->services
[db
] = result
;
230 __nss_configure_lookup (const char *dbname
, const char *service_line
)
233 nss_action_list result
;
234 struct nss_database_state
*local
;
236 /* Convert named database to index. */
237 db
= name_to_database_index (dbname
);
239 /* Not our database (e.g., sudoers). */
242 /* Force any load/cache/read whatever to happen, so we can override
244 __nss_database_get (db
, &result
);
246 local
= nss_database_state_get ();
248 result
= __nss_action_parse (service_line
);
252 atomic_store_release (&local
->data
.reload_disabled
, 1);
253 local
->data
.services
[db
] = result
;
256 __nss_database_custom
[db
] = true;
262 /* Iterate over the lines in FP, parse them, and store them in DATA.
263 Return false on memory allocation failure, true on success. */
265 nss_database_reload_1 (struct nss_database_data
*data
, FILE *fp
)
268 size_t line_allocated
= 0;
273 ssize_t ret
= __getline (&line
, &line_allocated
, fp
);
274 if (__ferror_unlocked (fp
))
276 if (__feof_unlocked (fp
))
282 (void) ret
; /* For NDEBUG builds. */
284 if (!process_line (data
, line
))
293 nss_database_reload (struct nss_database_data
*staging
,
294 struct file_change_detection
*initial
)
296 FILE *fp
= fopen (_PATH_NSSWITCH_CONF
, "rce");
306 /* Ignore these errors. They are persistent errors caused
307 by file system contents. */
310 /* Other errors refer to resource allocation problems and
311 need to be handled by the application. */
315 /* No other threads have access to fp. */
316 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
320 ok
= nss_database_reload_1 (staging
, fp
);
322 /* Apply defaults. */
325 struct nss_database_default_cache cache
= { };
326 for (int i
= 0; i
< NSS_DATABASE_COUNT
; ++i
)
327 if (staging
->services
[i
] == NULL
)
329 ok
= nss_database_select_default (&cache
, i
,
330 &staging
->services
[i
]);
337 ok
= __file_change_detection_for_fp (&staging
->nsswitch_conf
, fp
);
341 int saved_errno
= errno
;
343 __set_errno (saved_errno
);
346 if (ok
&& !__file_is_unchanged (&staging
->nsswitch_conf
, initial
))
347 /* Reload is required because the file changed while reading. */
348 staging
->nsswitch_conf
.size
= -1;
354 nss_database_check_reload_and_get (struct nss_database_state
*local
,
355 nss_action_list
*result
,
356 enum nss_database database_index
)
358 /* Acquire MO is needed because the thread that sets reload_disabled
359 may have loaded the configuration first, so synchronize with the
360 Release MO store there. */
361 if (atomic_load_acquire (&local
->data
.reload_disabled
))
363 *result
= local
->data
.services
[database_index
];
364 /* No reload, so there is no error. */
368 struct file_change_detection initial
;
369 if (!__file_change_detection_for_path (&initial
, _PATH_NSSWITCH_CONF
))
372 __libc_lock_lock (local
->lock
);
373 if (__file_is_unchanged (&initial
, &local
->data
.nsswitch_conf
))
375 /* Configuration is up-to-date. Read it and return it to the
377 *result
= local
->data
.services
[database_index
];
378 __libc_lock_unlock (local
->lock
);
381 __libc_lock_unlock (local
->lock
);
383 /* Avoid overwriting the global configuration until we have loaded
384 everything successfully. Otherwise, if the file change
385 information changes back to what is in the global configuration,
386 the lookups would use the partially-written configuration. */
387 struct nss_database_data staging
= { .initialized
= true, };
389 bool ok
= nss_database_reload (&staging
, &initial
);
393 __libc_lock_lock (local
->lock
);
395 /* See above for memory order. */
396 if (!atomic_load_acquire (&local
->data
.reload_disabled
))
397 /* This may go back in time if another thread beats this
398 thread with the update, but in this case, a reload happens
399 on the next NSS call. */
400 local
->data
= staging
;
402 *result
= local
->data
.services
[database_index
];
403 __libc_lock_unlock (local
->lock
);
410 __nss_database_get (enum nss_database db
, nss_action_list
*actions
)
412 struct nss_database_state
*local
= nss_database_state_get ();
413 return nss_database_check_reload_and_get (local
, actions
, db
);
417 __nss_database_get_noreload (enum nss_database db
)
419 /* There must have been a previous __nss_database_get call. */
420 struct nss_database_state
*local
= atomic_load_acquire (&global_database_state
);
421 assert (local
!= NULL
);
423 __libc_lock_lock (local
->lock
);
424 nss_action_list result
= local
->data
.services
[db
];
425 __libc_lock_unlock (local
->lock
);
429 void __libc_freeres_fn_section
430 __nss_database_freeres (void)
432 free (global_database_state
);
433 global_database_state
= NULL
;
437 __nss_database_fork_prepare_parent (struct nss_database_data
*data
)
439 /* Do not use allocate_once to trigger loading unnecessarily. */
440 struct nss_database_state
*local
= atomic_load_acquire (&global_database_state
);
442 data
->initialized
= false;
445 /* Make a copy of the configuration. This approach was chosen
446 because it avoids acquiring the lock during the actual
448 __libc_lock_lock (local
->lock
);
450 __libc_lock_unlock (local
->lock
);
455 __nss_database_fork_subprocess (struct nss_database_data
*data
)
457 struct nss_database_state
*local
= atomic_load_acquire (&global_database_state
);
458 if (data
->initialized
)
460 /* Restore the state at the point of the fork. */
461 assert (local
!= NULL
);
463 __libc_lock_init (local
->lock
);
465 else if (local
!= NULL
)
466 /* The NSS configuration was loaded concurrently during fork. We
467 do not know its state, so we need to discard it. */
468 global_database_state
= NULL
;