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