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
59int do_shutdown;
60int disabled_passwd;
61int disabled_group;
62
63typedef 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
75static run_modes run_mode = RUN_DAEMONIZE;
76
77static const char *conffile = _PATH_NSCDCONF;
78
79static const char *print_cache = NULL;
80
81time_t start_time;
82
83uintptr_t pagesize_m1;
84
85int paranoia;
86time_t restart_time;
87time_t restart_interval = RESTART_INTERVAL;
88const char *oldcwd;
89uid_t old_uid;
90gid_t old_gid;
91
92static int check_pid (const char *file);
93static int write_pid (const char *file);
94static int monitor_child (int fd);
95
96/* Name and version of program. */
97static void print_version (FILE *stream, struct argp_state *state);
98void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
99
100/* Function to print some extra text in the help message. */
101static char *more_help (int key, const char *text, void *input);
102
103/* Definitions of arguments for argp functions. */
104static 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. */
125static const char doc[] = N_("Name Service Cache Daemon.");
126
127/* Prototype for option handler. */
128static error_t parse_opt (int key, char *arg, struct argp_state *state);
129
130/* Data structure to communicate with argp functions. */
131static struct argp argp =
132{
133 options, parse_opt, NULL, doc, NULL, more_help
134};
135
136/* True if only statistics are requested. */
137static bool get_stats;
138static int parent_fd = -1;
139
140int
141main (int argc, char **argv)
142{
143 int remaining;
144
145 /* Set locale via LC_ALL. */
146 setlocale (LC_ALL, locale: "");
147 /* Set the text message domain. */
148 textdomain (PACKAGE);
149
150 /* Determine if the kernel has SELinux support. */
151 nscd_selinux_enabled (selinux_enabled: &selinux_enabled);
152
153 /* Parse and process arguments. */
154 argp_parse (argp: &argp, argc: argc, argv: argv, flags: 0, arg_index: &remaining, NULL);
155
156 if (remaining != argc)
157 {
158 error (status: 0, errnum: 0, gettext ("wrong number of arguments"));
159 argp_help (argp: &argp, stdout, ARGP_HELP_SEE, name: program_invocation_short_name);
160 exit (status: 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 (name: print_cache);
167
168 /* Read the configuration file. */
169 if (nscd_parse_file (fname: conffile, dbs) != 0)
170 /* We couldn't read the configuration file. We don't start the
171 server. */
172 error (EXIT_FAILURE, errnum: 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, errnum: 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 (pipedes: 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: fd[1]);
211 exit (status: monitor_child (fd: fd[0]));
212 }
213 else
214 {
215 /* The child only writes to the parent. */
216 close (fd: 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 (fd: nullfd, buf: &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 (fd: nullfd, STDIN_FILENO);
234 (void) dup2 (fd: nullfd, STDOUT_FILENO);
235 (void) dup2 (fd: nullfd, STDERR_FILENO);
236
237 if (nullfd > 2)
238 close (fd: nullfd);
239 }
240 else
241 {
242 /* Ugh, somebody is trying to play a trick on us. */
243 close (fd: nullfd);
244 nullfd = -1;
245 }
246 }
247 int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
248
249 DIR *d = opendir (name: "/proc/self/fd");
250 if (d != NULL)
251 {
252 struct dirent64 *dirent;
253 int dfdn = dirfd (d);
254
255 while ((dirent = readdir64 (dirp: d)) != NULL)
256 {
257 char *endp;
258 long int fdn = strtol (nptr: dirent->d_name, endptr: &endp, base: 10);
259
260 if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd
261 && fdn != parent_fd)
262 close (fd: (int) fdn);
263 }
264
265 closedir (dirp: d);
266 }
267 else
268 for (i = min_close_fd; i < getdtablesize (); i++)
269 if (i != parent_fd)
270 close (fd: i);
271
272 setsid ();
273
274 if (chdir (path: "/") != 0)
275 do_exit (EXIT_FAILURE, errno,
276 _("cannot change current working directory to \"/\""));
277
278 openlog (ident: "nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
279
280 if (write_pid (_PATH_NSCDPID) < 0)
281 dbg_log (str: "%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, handler: termination_handler);
296 signal (SIGQUIT, handler: termination_handler);
297 signal (SIGTERM, handler: 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
335static void __attribute__ ((noreturn))
336invalidate_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 (s: 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 (dest: reqdata->dbname, src: dbname, len: 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 (fd: sock);
363 error (EXIT_FAILURE, errnum: 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 (fd: sock);
374 error (EXIT_FAILURE, errnum: err, _("cannot read invalidate ACK"));
375 }
376
377 close (fd: sock);
378
379 if (resp != 0)
380 error (EXIT_FAILURE, errnum: resp, _("invalidation failed"));
381
382 exit (status: 0);
383}
384
385static void __attribute__ ((noreturn))
386send_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 (fd: sock);
401 exit (status: nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
402}
403
404/* Handle program arguments. */
405static error_t
406parse_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 (status: 4, errnum: 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 (s1: arg, s2: dbnames[cnt]) == 0)
445 break;
446
447 if (cnt == lastdb)
448 {
449 argp_error (state: state, _("'%s' is not a known database"), arg);
450 return EINVAL;
451 }
452 }
453 if (getuid () != 0)
454 error (status: 4, errnum: 0, _("Only root is allowed to use this option!"));
455 else
456 invalidate_db (dbname: arg);
457 break;
458
459 case 't':
460 nthreads = atol (nptr: arg);
461 break;
462
463 case 'S':
464 error (status: 0, errnum: 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. */
475static char *
476more_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 (ptr: tables);
491 if (more_tables == NULL)
492 return NULL;
493 tables = more_tables;
494 }
495
496 char *tp;
497 if (asprintf (&tp, gettext ("\
498Supported tables:\n\
499%s\n\
500\n\
501For bug reporting instructions, please see:\n\
502%s.\n\
503"), tables, REPORT_BUGS_TO) < 0)
504 tp = NULL;
505 free (ptr: tables);
506 return tp;
507 }
508
509 default:
510 break;
511 }
512
513 return (char *) text;
514}
515
516/* Print the version information. */
517static void
518print_version (FILE *stream, struct argp_state *state)
519{
520 fprintf (stream, "nscd %s%s\n", PKGVERSION, VERSION);
521 fprintf (stream, gettext ("\
522Copyright (C) %s Free Software Foundation, Inc.\n\
523This is free software; see the source for copying conditions. There is NO\n\
524warranty; 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. */
532int
533nscd_open_socket (void)
534{
535 struct sockaddr_un addr;
536 int sock;
537
538 sock = socket (PF_UNIX, SOCK_STREAM, protocol: 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 (dest: addr.sun_path, _PATH_NSCDSOCKET);
545 if (connect (fd: sock, addr: (struct sockaddr *) &addr, len: sizeof (addr)) < 0)
546 {
547 close (fd: sock);
548 return -1;
549 }
550
551 return sock;
552}
553
554
555/* Cleanup. */
556void
557termination_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 (addr: dbs[cnt].head, len: 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. */
587static int
588check_pid (const char *file)
589{
590 FILE *fp;
591
592 fp = fopen (filename: file, modes: "r");
593 if (fp)
594 {
595 pid_t pid;
596 int n;
597
598 n = fscanf (stream: fp, format: "%d", &pid);
599 fclose (stream: 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: pid, sig: 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. */
614static int
615write_pid (const char *file)
616{
617 FILE *fp;
618
619 fp = fopen (filename: file, modes: "w");
620 if (fp == NULL)
621 return -1;
622
623 fprintf (fp, "%d\n", getpid ());
624
625 int result = fflush (stream: fp) || ferror (stream: fp) ? -1 : 0;
626
627 fclose (stream: fp);
628
629 return result;
630}
631
632static int
633monitor_child (int fd)
634{
635 int child_ret = 0;
636 int ret = read (fd: fd, buf: &child_ret, nbytes: 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 (stat_loc: &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: fd);
665
666 return child_ret;
667}
668
669void
670do_exit (int child_ret, int errnum, const char *format, ...)
671{
672 if (parent_fd != -1)
673 {
674 int ret __attribute__ ((unused));
675 ret = write (fd: parent_fd, buf: &child_ret, n: sizeof (child_ret));
676 assert (ret == sizeof (child_ret));
677 close (fd: 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, fmt: format, ap: argp);
691 va_end (argp);
692
693 fprintf (stderr, ": %s\n", strerror (errnum));
694 fflush (stderr);
695 }
696
697 /* Finally, exit. */
698 exit (status: child_ret);
699}
700
701void
702notify_parent (int child_ret)
703{
704 if (parent_fd == -1)
705 return;
706
707 int ret __attribute__ ((unused));
708 ret = write (fd: parent_fd, buf: &child_ret, n: sizeof (child_ret));
709 assert (ret == sizeof (child_ret));
710 close (fd: parent_fd);
711 parent_fd = -1;
712}
713

source code of glibc/nscd/nscd.c