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 
32 struct 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 *".  */
47 static void *global_database_state;
48 
49 /* Allocate and return pointer to nss_database_state object or
50    on failure return NULL.  */
51 static void *
global_state_allocate(void * closure)52 global_state_allocate (void *closure)
53 {
54   struct nss_database_state *result =  malloc (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.  */
70 static struct nss_database_state *
nss_database_state_get(void)71 nss_database_state_get (void)
72 {
73   return allocate_once (&global_database_state, 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.  */
79 enum 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.  */
93 static 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 
108 struct nss_database_default_cache
109 {
110   nss_action_list caches[NSS_DATABASE_DEFAULT_COUNT];
111 };
112 
113 static bool
nss_database_select_default(struct nss_database_default_cache * cache,enum nss_database db,nss_action_list * result)114 nss_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.  */
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
182 
183 static const database_name nss_database_name_array[] =
184   {
185 #define DEFINE_DATABASE(name) #name,
186 #include "databases.def"
187 #undef DEFINE_DATABASE
188   };
189 
190 static int
name_search(const void * left,const void * right)191 name_search (const void *left, const void *right)
192 {
193   return strcmp (left, right);
194 }
195 
196 static int
name_to_database_index(const char * name)197 name_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 
207 static bool
process_line(struct nss_database_data * data,char * line)208 process_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 
239 int
__nss_configure_lookup(const char * dbname,const char * service_line)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 (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 (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.  */
274 static bool
nss_database_reload_1(struct nss_database_data * data,FILE * fp)275 nss_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 (&line, &line_allocated, fp);
284       if (__ferror_unlocked (fp))
285         break;
286       if (__feof_unlocked (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 (line);
299   return result;
300 }
301 
302 static bool
nss_database_reload(struct nss_database_data * staging,struct file_change_detection * initial)303 nss_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 (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, i,
369                                               &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 
392 static bool
nss_database_check_reload_and_get(struct nss_database_state * local,nss_action_list * result,enum nss_database database_index)393 nss_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, &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 
477 bool
__nss_database_get(enum nss_database db,nss_action_list * actions)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, actions, db);
482 }
libc_hidden_def(__nss_database_get)483 libc_hidden_def (__nss_database_get)
484 
485 nss_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 
498 void __libc_freeres_fn_section
__nss_database_freeres(void)499 __nss_database_freeres (void)
500 {
501   free (global_database_state);
502   global_database_state = NULL;
503 }
504 
505 void
__nss_database_fork_prepare_parent(struct nss_database_data * data)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 
523 void
__nss_database_fork_subprocess(struct nss_database_data * data)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