1 /* Copyright (c) 1998-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published
6    by the Free Software Foundation; version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts.  */
18 
19 #include <argp.h>
20 #include <assert.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <error.h>
24 #include <fcntl.h>
25 #include <libintl.h>
26 #include <locale.h>
27 #include <paths.h>
28 #include <pthread.h>
29 #include <signal.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <syslog.h>
35 #include <unistd.h>
36 #include <sys/mman.h>
37 #include <sys/socket.h>
38 #include <sys/stat.h>
39 #include <sys/uio.h>
40 #include <sys/un.h>
41 #include <sys/wait.h>
42 #include <stdarg.h>
43 
44 #include "dbg_log.h"
45 #include "nscd.h"
46 #include "selinux.h"
47 #include "../nss/nsswitch.h"
48 #include <device-nrs.h>
49 #ifdef HAVE_INOTIFY
50 # include <sys/inotify.h>
51 #endif
52 #include <kernel-features.h>
53 
54 /* Get libc version number.  */
55 #include <version.h>
56 
57 #define PACKAGE _libc_intl_domainname
58 
59 int do_shutdown;
60 int disabled_passwd;
61 int disabled_group;
62 
63 typedef enum
64 {
65   /* Running in background as daemon.  */
66   RUN_DAEMONIZE,
67   /* Running in foreground but otherwise behave like a daemon,
68      i.e., detach from terminal and use syslog.  This allows
69      better integration with services like systemd.  */
70   RUN_FOREGROUND,
71   /* Run in foreground in debug mode.  */
72   RUN_DEBUG
73 } run_modes;
74 
75 static run_modes run_mode = RUN_DAEMONIZE;
76 
77 static const char *conffile = _PATH_NSCDCONF;
78 
79 static const char *print_cache = NULL;
80 
81 time_t start_time;
82 
83 uintptr_t pagesize_m1;
84 
85 int paranoia;
86 time_t restart_time;
87 time_t restart_interval = RESTART_INTERVAL;
88 const char *oldcwd;
89 uid_t old_uid;
90 gid_t old_gid;
91 
92 static int check_pid (const char *file);
93 static int write_pid (const char *file);
94 static int monitor_child (int fd);
95 
96 /* Name and version of program.  */
97 static void print_version (FILE *stream, struct argp_state *state);
98 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
99 
100 /* Function to print some extra text in the help message.  */
101 static char *more_help (int key, const char *text, void *input);
102 
103 /* Definitions of arguments for argp functions.  */
104 static const struct argp_option options[] =
105 {
106   { "config-file", 'f', N_("NAME"), 0,
107     N_("Read configuration data from NAME") },
108   { "debug", 'd', NULL, 0,
109     N_("Do not fork and display messages on the current tty") },
110   { "print", 'p', N_("NAME"), 0,
111     N_("Print contents of the offline cache file NAME") },
112   { "foreground", 'F', NULL, 0,
113     N_("Do not fork, but otherwise behave like a daemon") },
114   { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
115   { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
116   { "statistics", 'g', NULL, 0, N_("Print current configuration statistics") },
117   { "invalidate", 'i', N_("TABLE"), 0,
118     N_("Invalidate the specified cache") },
119   { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
120     N_("Use separate cache for each user")},
121   { NULL, 0, NULL, 0, NULL }
122 };
123 
124 /* Short description of program.  */
125 static const char doc[] = N_("Name Service Cache Daemon.");
126 
127 /* Prototype for option handler.  */
128 static error_t parse_opt (int key, char *arg, struct argp_state *state);
129 
130 /* Data structure to communicate with argp functions.  */
131 static struct argp argp =
132 {
133   options, parse_opt, NULL, doc, NULL, more_help
134 };
135 
136 /* True if only statistics are requested.  */
137 static bool get_stats;
138 static int parent_fd = -1;
139 
140 int
main(int argc,char ** argv)141 main (int argc, char **argv)
142 {
143   int remaining;
144 
145   /* Set locale via LC_ALL.  */
146   setlocale (LC_ALL, "");
147   /* Set the text message domain.  */
148   textdomain (PACKAGE);
149 
150   /* Determine if the kernel has SELinux support.  */
151   nscd_selinux_enabled (&selinux_enabled);
152 
153   /* Parse and process arguments.  */
154   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
155 
156   if (remaining != argc)
157     {
158       error (0, 0, gettext ("wrong number of arguments"));
159       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
160       exit (1);
161     }
162 
163   /* Print the contents of the indicated cache file.  */
164   if (print_cache != NULL)
165     /* Does not return.  */
166     nscd_print_cache (print_cache);
167 
168   /* Read the configuration file.  */
169   if (nscd_parse_file (conffile, dbs) != 0)
170     /* We couldn't read the configuration file.  We don't start the
171        server.  */
172     error (EXIT_FAILURE, 0,
173 	   _("failure while reading configuration file; this is fatal"));
174 
175   /* Do we only get statistics?  */
176   if (get_stats)
177     /* Does not return.  */
178     receive_print_stats ();
179 
180   /* Check if we are already running. */
181   if (check_pid (_PATH_NSCDPID))
182     error (EXIT_FAILURE, 0, _("already running"));
183 
184   /* Remember when we started.  */
185   start_time = time (NULL);
186 
187   /* Determine page size.  */
188   pagesize_m1 = getpagesize () - 1;
189 
190   if (run_mode == RUN_DAEMONIZE || run_mode == RUN_FOREGROUND)
191     {
192       int i;
193       pid_t pid;
194 
195       /* Behave like a daemon.  */
196       if (run_mode == RUN_DAEMONIZE)
197 	{
198 	  int fd[2];
199 
200 	  if (pipe (fd) != 0)
201 	    error (EXIT_FAILURE, errno,
202 		   _("cannot create a pipe to talk to the child"));
203 
204 	  pid = fork ();
205 	  if (pid == -1)
206 	    error (EXIT_FAILURE, errno, _("cannot fork"));
207 	  if (pid != 0)
208 	    {
209 	      /* The parent only reads from the child.  */
210 	      close (fd[1]);
211 	      exit (monitor_child (fd[0]));
212 	    }
213 	  else
214 	    {
215 	      /* The child only writes to the parent.  */
216 	      close (fd[0]);
217 	      parent_fd = fd[1];
218 	    }
219 	}
220 
221       int nullfd = open (_PATH_DEVNULL, O_RDWR);
222       if (nullfd != -1)
223 	{
224 	  struct stat64 st;
225 
226 	  if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
227 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
228 	      && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
229 #endif
230 	      )
231 	    {
232 	      /* It is the /dev/null special device alright.  */
233 	      (void) dup2 (nullfd, STDIN_FILENO);
234 	      (void) dup2 (nullfd, STDOUT_FILENO);
235 	      (void) dup2 (nullfd, STDERR_FILENO);
236 
237 	      if (nullfd > 2)
238 		close (nullfd);
239 	    }
240 	  else
241 	    {
242 	      /* Ugh, somebody is trying to play a trick on us.  */
243 	      close (nullfd);
244 	      nullfd = -1;
245 	    }
246 	}
247       int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
248 
249       DIR *d = opendir ("/proc/self/fd");
250       if (d != NULL)
251 	{
252 	  struct dirent64 *dirent;
253 	  int dfdn = dirfd (d);
254 
255 	  while ((dirent = readdir64 (d)) != NULL)
256 	    {
257 	      char *endp;
258 	      long int fdn = strtol (dirent->d_name, &endp, 10);
259 
260 	      if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd
261 		  && fdn != parent_fd)
262 		close ((int) fdn);
263 	    }
264 
265 	  closedir (d);
266 	}
267       else
268 	for (i = min_close_fd; i < getdtablesize (); i++)
269 	  if (i != parent_fd)
270 	    close (i);
271 
272       setsid ();
273 
274       if (chdir ("/") != 0)
275 	do_exit (EXIT_FAILURE, errno,
276 		 _("cannot change current working directory to \"/\""));
277 
278       openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
279 
280       if (write_pid (_PATH_NSCDPID) < 0)
281 	dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
282 
283       if (!init_logfile ())
284 	dbg_log (_("Could not create log file"));
285 
286       /* Ignore job control signals.  */
287       signal (SIGTTOU, SIG_IGN);
288       signal (SIGTTIN, SIG_IGN);
289       signal (SIGTSTP, SIG_IGN);
290     }
291   else
292     /* In debug mode we are not paranoid.  */
293     paranoia = 0;
294 
295   signal (SIGINT, termination_handler);
296   signal (SIGQUIT, termination_handler);
297   signal (SIGTERM, termination_handler);
298   signal (SIGPIPE, SIG_IGN);
299 
300   /* Cleanup files created by a previous 'bind'.  */
301   unlink (_PATH_NSCDSOCKET);
302 
303 #ifdef HAVE_INOTIFY
304   /* Use inotify to recognize changed files.  */
305   inotify_fd = inotify_init1 (IN_NONBLOCK);
306 # ifndef __ASSUME_IN_NONBLOCK
307   if (inotify_fd == -1 && errno == ENOSYS)
308     {
309       inotify_fd = inotify_init ();
310       if (inotify_fd != -1)
311 	fcntl (inotify_fd, F_SETFL, O_RDONLY | O_NONBLOCK);
312     }
313 # endif
314 #endif
315 
316 #ifdef USE_NSCD
317   /* Make sure we do not get recursive calls.  */
318   __nss_disable_nscd (register_traced_file);
319 #endif
320 
321   /* Init databases.  */
322   nscd_init ();
323 
324   /* Start the SELinux AVC.  */
325   if (selinux_enabled)
326     nscd_avc_init ();
327 
328   /* Handle incoming requests */
329   start_threads ();
330 
331   return 0;
332 }
333 
334 
335 static void __attribute__ ((noreturn))
invalidate_db(const char * dbname)336 invalidate_db (const char *dbname)
337 {
338   int sock = nscd_open_socket ();
339 
340   if (sock == -1)
341     exit (EXIT_FAILURE);
342 
343   size_t dbname_len = strlen (dbname) + 1;
344   size_t reqlen = sizeof (request_header) + dbname_len;
345   struct
346   {
347     request_header req;
348     char dbname[];
349   } *reqdata = alloca (reqlen);
350 
351   reqdata->req.key_len = dbname_len;
352   reqdata->req.version = NSCD_VERSION;
353   reqdata->req.type = INVALIDATE;
354   memcpy (reqdata->dbname, dbname, dbname_len);
355 
356   ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, reqdata, reqlen,
357 					     MSG_NOSIGNAL));
358 
359   if (nbytes != reqlen)
360     {
361       int err = errno;
362       close (sock);
363       error (EXIT_FAILURE, err, _("write incomplete"));
364     }
365 
366   /* Wait for ack.  Older nscd just closed the socket when
367      prune_cache finished, silently ignore that.  */
368   int32_t resp = 0;
369   nbytes = TEMP_FAILURE_RETRY (read (sock, &resp, sizeof (resp)));
370   if (nbytes != 0 && nbytes != sizeof (resp))
371     {
372       int err = errno;
373       close (sock);
374       error (EXIT_FAILURE, err, _("cannot read invalidate ACK"));
375     }
376 
377   close (sock);
378 
379   if (resp != 0)
380     error (EXIT_FAILURE, resp, _("invalidation failed"));
381 
382   exit (0);
383 }
384 
385 static void __attribute__ ((noreturn))
send_shutdown(void)386 send_shutdown (void)
387 {
388   int sock = nscd_open_socket ();
389 
390   if (sock == -1)
391     exit (EXIT_FAILURE);
392 
393   request_header req;
394   req.version = NSCD_VERSION;
395   req.type = SHUTDOWN;
396   req.key_len = 0;
397 
398   ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &req, sizeof req,
399                                              MSG_NOSIGNAL));
400   close (sock);
401   exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
402 }
403 
404 /* Handle program arguments.  */
405 static error_t
parse_opt(int key,char * arg,struct argp_state * state)406 parse_opt (int key, char *arg, struct argp_state *state)
407 {
408   switch (key)
409     {
410     case 'd':
411       ++debug_level;
412       run_mode = RUN_DEBUG;
413       break;
414 
415     case 'p':
416       print_cache = arg;
417       break;
418 
419     case 'F':
420       run_mode = RUN_FOREGROUND;
421       break;
422 
423     case 'f':
424       conffile = arg;
425       break;
426 
427     case 'K':
428       if (getuid () != 0)
429 	error (4, 0, _("Only root is allowed to use this option!"));
430       else
431         send_shutdown ();
432       break;
433 
434     case 'g':
435       get_stats = true;
436       break;
437 
438     case 'i':
439       {
440         /* Validate the database name.  */
441 
442         dbtype cnt;
443         for (cnt = pwddb; cnt < lastdb; ++cnt)
444           if (strcmp (arg, dbnames[cnt]) == 0)
445             break;
446 
447         if (cnt == lastdb)
448           {
449             argp_error (state, _("'%s' is not a known database"), arg);
450             return EINVAL;
451           }
452       }
453       if (getuid () != 0)
454 	error (4, 0, _("Only root is allowed to use this option!"));
455       else
456         invalidate_db (arg);
457       break;
458 
459     case 't':
460       nthreads = atol (arg);
461       break;
462 
463     case 'S':
464       error (0, 0, _("secure services not implemented anymore"));
465       break;
466 
467     default:
468       return ARGP_ERR_UNKNOWN;
469     }
470 
471   return 0;
472 }
473 
474 /* Print bug-reporting information in the help message.  */
475 static char *
more_help(int key,const char * text,void * input)476 more_help (int key, const char *text, void *input)
477 {
478   switch (key)
479     {
480     case ARGP_KEY_HELP_EXTRA:
481       {
482 	/* We print some extra information.  */
483 
484 	char *tables = xstrdup (dbnames[0]);
485 	for (dbtype i = 1; i < lastdb; ++i)
486 	  {
487 	    char *more_tables;
488 	    if (asprintf (&more_tables, "%s %s", tables, dbnames[i]) < 0)
489 	      more_tables = NULL;
490 	    free (tables);
491 	    if (more_tables == NULL)
492 	      return NULL;
493 	    tables = more_tables;
494 	  }
495 
496 	char *tp;
497 	if (asprintf (&tp, gettext ("\
498 Supported tables:\n\
499 %s\n\
500 \n\
501 For bug reporting instructions, please see:\n\
502 %s.\n\
503 "), tables, REPORT_BUGS_TO) < 0)
504 	  tp = NULL;
505 	free (tables);
506 	return tp;
507       }
508 
509     default:
510       break;
511     }
512 
513   return (char *) text;
514 }
515 
516 /* Print the version information.  */
517 static void
print_version(FILE * stream,struct argp_state * state)518 print_version (FILE *stream, struct argp_state *state)
519 {
520   fprintf (stream, "nscd %s%s\n", PKGVERSION, VERSION);
521   fprintf (stream, gettext ("\
522 Copyright (C) %s Free Software Foundation, Inc.\n\
523 This is free software; see the source for copying conditions.  There is NO\n\
524 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
525 "), "2022");
526   fprintf (stream, gettext ("Written by %s.\n"),
527 	   "Thorsten Kukuk and Ulrich Drepper");
528 }
529 
530 
531 /* Create a socket connected to a name.  */
532 int
nscd_open_socket(void)533 nscd_open_socket (void)
534 {
535   struct sockaddr_un addr;
536   int sock;
537 
538   sock = socket (PF_UNIX, SOCK_STREAM, 0);
539   if (sock < 0)
540     return -1;
541 
542   addr.sun_family = AF_UNIX;
543   assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
544   strcpy (addr.sun_path, _PATH_NSCDSOCKET);
545   if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
546     {
547       close (sock);
548       return -1;
549     }
550 
551   return sock;
552 }
553 
554 
555 /* Cleanup.  */
556 void
termination_handler(int signum)557 termination_handler (int signum)
558 {
559   close_sockets ();
560 
561   /* Clean up the file created by 'bind'.  */
562   unlink (_PATH_NSCDSOCKET);
563 
564   /* Clean up pid file.  */
565   unlink (_PATH_NSCDPID);
566 
567   // XXX Terminate threads.
568 
569   /* Synchronize memory.  */
570   for (int cnt = 0; cnt < lastdb; ++cnt)
571     {
572       if (!dbs[cnt].enabled || dbs[cnt].head == NULL)
573 	continue;
574 
575       /* Make sure nobody keeps using the database.  */
576       dbs[cnt].head->timestamp = 0;
577 
578       if (dbs[cnt].persistent)
579 	// XXX async OK?
580 	msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
581     }
582 
583   _exit (EXIT_SUCCESS);
584 }
585 
586 /* Returns 1 if the process in pid file FILE is running, 0 if not.  */
587 static int
check_pid(const char * file)588 check_pid (const char *file)
589 {
590   FILE *fp;
591 
592   fp = fopen (file, "r");
593   if (fp)
594     {
595       pid_t pid;
596       int n;
597 
598       n = fscanf (fp, "%d", &pid);
599       fclose (fp);
600 
601       /* If we cannot parse the file default to assuming nscd runs.
602 	 If the PID is alive, assume it is running.  That all unless
603 	 the PID is the same as the current process' since tha latter
604 	 can mean we re-exec.  */
605       if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
606 	return 1;
607     }
608 
609   return 0;
610 }
611 
612 /* Write the current process id to the file FILE.
613    Returns 0 if successful, -1 if not.  */
614 static int
write_pid(const char * file)615 write_pid (const char *file)
616 {
617   FILE *fp;
618 
619   fp = fopen (file, "w");
620   if (fp == NULL)
621     return -1;
622 
623   fprintf (fp, "%d\n", getpid ());
624 
625   int result = fflush (fp) || ferror (fp) ? -1 : 0;
626 
627   fclose (fp);
628 
629   return result;
630 }
631 
632 static int
monitor_child(int fd)633 monitor_child (int fd)
634 {
635   int child_ret = 0;
636   int ret = read (fd, &child_ret, sizeof (child_ret));
637 
638   /* The child terminated with an error, either via exit or some other abnormal
639      method, like a segfault.  */
640   if (ret <= 0 || child_ret != 0)
641     {
642       int status;
643       int err = wait (&status);
644 
645       if (err < 0)
646 	{
647 	  fprintf (stderr, _("'wait' failed\n"));
648 	  return 1;
649 	}
650 
651       if (WIFEXITED (status))
652 	{
653 	  child_ret = WEXITSTATUS (status);
654 	  fprintf (stderr, _("child exited with status %d\n"), child_ret);
655 	}
656       if (WIFSIGNALED (status))
657 	{
658 	  child_ret = WTERMSIG (status);
659 	  fprintf (stderr, _("child terminated by signal %d\n"), child_ret);
660 	}
661     }
662 
663   /* We have the child status, so exit with that code.  */
664   close (fd);
665 
666   return child_ret;
667 }
668 
669 void
do_exit(int child_ret,int errnum,const char * format,...)670 do_exit (int child_ret, int errnum, const char *format, ...)
671 {
672   if (parent_fd != -1)
673     {
674       int ret __attribute__ ((unused));
675       ret = write (parent_fd, &child_ret, sizeof (child_ret));
676       assert (ret == sizeof (child_ret));
677       close (parent_fd);
678     }
679 
680   if (format != NULL)
681     {
682       /* Emulate error() since we don't have a va_list variant for it.  */
683       va_list argp;
684 
685       fflush (stdout);
686 
687       fprintf (stderr, "%s: ", program_invocation_name);
688 
689       va_start (argp, format);
690       vfprintf (stderr, format, argp);
691       va_end (argp);
692 
693       fprintf (stderr, ": %s\n", strerror (errnum));
694       fflush (stderr);
695     }
696 
697   /* Finally, exit.  */
698   exit (child_ret);
699 }
700 
701 void
notify_parent(int child_ret)702 notify_parent (int child_ret)
703 {
704   if (parent_fd == -1)
705     return;
706 
707   int ret __attribute__ ((unused));
708   ret = write (parent_fd, &child_ret, sizeof (child_ret));
709   assert (ret == sizeof (child_ret));
710   close (parent_fd);
711   parent_fd = -1;
712 }
713