(root)/
glibc-2.38/
nscd/
nscd.c
       1  /* Copyright (c) 1998-2023 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
     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))
     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))
     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
     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 *
     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
     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  "), "2023");
     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
     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
     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
     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 the 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
     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
     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
     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
     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  }