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