Update.
Implement paranoia mode. * nscd/connections.c (nscd_init): Mark database and socket descriptors as close on exec. (restart): New function. (restart_p): New function. (nscd_run): Add missing descrement of nready in case readylist is empty. (main_loop_poll): Call restart_p and restart. (main_loop_epoll): Likewise. (begin_drop_privileges): Save original UID and GID. * nscd/nscd.c: Define new variables paranoia, restart_time, restart_interval, oldcwd, old_gid, old_uid. (main): Disable paranoia mode if we are not forking. (check_pid): When re-execing, the PID file contains the same PID as the current process. Do not fail in this case. * nscd/nscd.conf: Add paranoia and restart-interval entries. * nscd/nscd.h: Define RESTART_INTERVAL. Declare new variables. * nscd/nscd_conf.c: Parse paranoia and restart-internal configurations. * nscd/nscd_stat.c: Print paranoia and restart-internal values.
This commit is contained in:
parent
fc03df7aa6
commit
4401d75905
20
ChangeLog
20
ChangeLog
|
@ -1,5 +1,25 @@
|
|||
2004-10-03 Ulrich Drepper <drepper@redhat.com>
|
||||
|
||||
Implement paranoia mode.
|
||||
* nscd/connections.c (nscd_init): Mark database and socket descriptors
|
||||
as close on exec.
|
||||
(restart): New function.
|
||||
(restart_p): New function.
|
||||
(nscd_run): Add missing descrement of nready in case readylist is
|
||||
empty.
|
||||
(main_loop_poll): Call restart_p and restart.
|
||||
(main_loop_epoll): Likewise.
|
||||
(begin_drop_privileges): Save original UID and GID.
|
||||
* nscd/nscd.c: Define new variables paranoia, restart_time,
|
||||
restart_interval, oldcwd, old_gid, old_uid.
|
||||
(main): Disable paranoia mode if we are not forking.
|
||||
(check_pid): When re-execing, the PID file contains the same PID as
|
||||
the current process. Do not fail in this case.
|
||||
* nscd/nscd.conf: Add paranoia and restart-interval entries.
|
||||
* nscd/nscd.h: Define RESTART_INTERVAL. Declare new variables.
|
||||
* nscd/nscd_conf.c: Parse paranoia and restart-internal configurations.
|
||||
* nscd/nscd_stat.c: Print paranoia and restart-internal values.
|
||||
|
||||
* nscd/connections.c: Implement alternative loop for main thread
|
||||
which uses epoll.
|
||||
* sysdeps/unix/sysv/linux/Makefile [subdir=nscd]
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA. */
|
||||
|
||||
#include <alloca.h>
|
||||
#include <assert.h>
|
||||
#include <atomic.h>
|
||||
#include <error.h>
|
||||
|
@ -437,6 +438,18 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
|
|||
}
|
||||
}
|
||||
|
||||
if (paranoia
|
||||
&& ((dbs[cnt].wr_fd != -1
|
||||
&& fcntl (dbs[cnt].wr_fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||
|| (dbs[cnt].ro_fd != -1
|
||||
&& fcntl (dbs[cnt].ro_fd, F_SETFD, FD_CLOEXEC) == -1)))
|
||||
{
|
||||
dbg_log (_("\
|
||||
cannot set socket to close on exec: %s; disabling paranoia mode"),
|
||||
strerror (errno));
|
||||
paranoia = 0;
|
||||
}
|
||||
|
||||
if (dbs[cnt].head == NULL)
|
||||
{
|
||||
/* We do not use the persistent database. Just
|
||||
|
@ -493,11 +506,22 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
|
|||
exit (1);
|
||||
}
|
||||
|
||||
/* We don't wait for data otherwise races between threads can get
|
||||
them stuck on accept. */
|
||||
/* We don't want to get stuck on accept. */
|
||||
int fl = fcntl (sock, F_GETFL);
|
||||
if (fl != -1)
|
||||
fcntl (sock, F_SETFL, fl | O_NONBLOCK);
|
||||
if (fl == -1 || fcntl (sock, F_SETFL, fl | O_NONBLOCK) == -1)
|
||||
{
|
||||
dbg_log (_("cannot change socket to nonblocking mode: %s"),
|
||||
strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* The descriptor needs to be closed on exec. */
|
||||
if (paranoia && fcntl (sock, F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
dbg_log (_("cannot set socket to close on exec: %s"),
|
||||
strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Set permissions for the socket. */
|
||||
chmod (_PATH_NSCDSOCKET, DEFFILEMODE);
|
||||
|
@ -788,6 +812,138 @@ cannot handle old request version %d; current version is %d"),
|
|||
}
|
||||
|
||||
|
||||
/* Restart the process. */
|
||||
static void
|
||||
restart (void)
|
||||
{
|
||||
/* First determine the parameters. We do not use the parameters
|
||||
passed to main() since in case nscd is started by running the
|
||||
dynamic linker this will not work. Yes, this is not the usual
|
||||
case but nscd is part of glibc and we occasionally do this. */
|
||||
size_t buflen = 1024;
|
||||
char *buf = alloca (buflen);
|
||||
size_t readlen = 0;
|
||||
int fd = open ("/proc/self/cmdline", O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
dbg_log (_("\
|
||||
cannot open /proc/self/cmdline: %s; disabling paranoia mode"),
|
||||
strerror (errno));
|
||||
|
||||
paranoia = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + readlen,
|
||||
buflen - readlen));
|
||||
if (n == -1)
|
||||
{
|
||||
dbg_log (_("\
|
||||
cannot open /proc/self/cmdline: %s; disabling paranoia mode"),
|
||||
strerror (errno));
|
||||
|
||||
close (fd);
|
||||
paranoia = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
readlen += n;
|
||||
|
||||
if (readlen < buflen)
|
||||
break;
|
||||
|
||||
/* We might have to extend the buffer. */
|
||||
size_t old_buflen = buflen;
|
||||
char *newp = extend_alloca (buf, buflen, 2 * buflen);
|
||||
buf = memmove (newp, buf, old_buflen);
|
||||
}
|
||||
|
||||
close (fd);
|
||||
|
||||
/* Parse the command line. Worst case scenario: every two
|
||||
characters form one parameter (one character plus NUL). */
|
||||
char **argv = alloca ((readlen / 2 + 1) * sizeof (argv[0]));
|
||||
int argc = 0;
|
||||
|
||||
char *cp = buf;
|
||||
while (cp < buf + readlen)
|
||||
{
|
||||
argv[argc++] = cp;
|
||||
cp = (char *) rawmemchr (cp, '\0') + 1;
|
||||
}
|
||||
argv[argc] = NULL;
|
||||
|
||||
/* Second, change back to the old user if we changed it. */
|
||||
if (server_user != NULL)
|
||||
{
|
||||
if (setuid (old_uid) != 0)
|
||||
{
|
||||
dbg_log (_("\
|
||||
cannot change to old UID: %s; disabling paranoia mode"),
|
||||
strerror (errno));
|
||||
|
||||
paranoia = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (setgid (old_gid) != 0)
|
||||
{
|
||||
dbg_log (_("\
|
||||
cannot change to old GID: %s; disabling paranoia mode"),
|
||||
strerror (errno));
|
||||
|
||||
setuid (server_uid);
|
||||
paranoia = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Next change back to the old working directory. */
|
||||
if (chdir (oldcwd) == -1)
|
||||
{
|
||||
dbg_log (_("\
|
||||
cannot change to old working directory: %s; disabling paranoia mode"),
|
||||
strerror (errno));
|
||||
|
||||
if (server_user != NULL)
|
||||
{
|
||||
setuid (server_uid);
|
||||
setgid (server_gid);
|
||||
}
|
||||
paranoia = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Synchronize memory. */
|
||||
for (int cnt = 0; cnt < lastdb; ++cnt)
|
||||
{
|
||||
/* Make sure nobody keeps using the database. */
|
||||
dbs[cnt].head->timestamp = 0;
|
||||
|
||||
if (dbs[cnt].persistent)
|
||||
// XXX async OK?
|
||||
msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
|
||||
}
|
||||
|
||||
/* The preparations are done. */
|
||||
execv ("/proc/self/exe", argv);
|
||||
|
||||
/* If we come here, we will never be able to re-exec. */
|
||||
dbg_log (_("re-exec failed: %s; disabling paranoia mode"),
|
||||
strerror (errno));
|
||||
|
||||
if (server_user != NULL)
|
||||
{
|
||||
setuid (server_uid);
|
||||
setgid (server_gid);
|
||||
}
|
||||
chdir ("/");
|
||||
paranoia = 0;
|
||||
}
|
||||
|
||||
|
||||
/* List of file descriptors. */
|
||||
struct fdlist
|
||||
{
|
||||
|
@ -859,6 +1015,7 @@ nscd_run (void *p)
|
|||
just start pruning. */
|
||||
if (readylist == NULL && to == ETIMEDOUT)
|
||||
{
|
||||
--nready;
|
||||
pthread_mutex_unlock (&readylist_lock);
|
||||
goto only_prune;
|
||||
}
|
||||
|
@ -1059,7 +1216,16 @@ fd_ready (int fd)
|
|||
}
|
||||
|
||||
|
||||
/* Time a connection was accepted. */
|
||||
/* Check whether restarting should happen. */
|
||||
static inline int
|
||||
restart_p (time_t now)
|
||||
{
|
||||
return (paranoia && readylist == NULL && nready == nthreads
|
||||
&& now >= restart_time);
|
||||
}
|
||||
|
||||
|
||||
/* Array for times a connection was accepted. */
|
||||
static time_t *starttime;
|
||||
|
||||
|
||||
|
@ -1160,6 +1326,9 @@ main_loop_poll (void)
|
|||
while (conns[nused - 1].fd == -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (restart_p (now))
|
||||
restart ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1252,6 +1421,9 @@ main_loop_epoll (int efd)
|
|||
}
|
||||
else if (cnt != sock && starttime[cnt] == 0 && cnt == highest)
|
||||
--highest;
|
||||
|
||||
if (restart_p (now))
|
||||
restart ();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1347,6 +1519,13 @@ begin_drop_privileges (void)
|
|||
server_uid = pwd->pw_uid;
|
||||
server_gid = pwd->pw_gid;
|
||||
|
||||
/* Save the old UID/GID if we have to change back. */
|
||||
if (paranoia)
|
||||
{
|
||||
old_uid = getuid ();
|
||||
old_gid = getgid ();
|
||||
}
|
||||
|
||||
if (getgrouplist (server_user, server_gid, NULL, &server_ngroups) == 0)
|
||||
{
|
||||
/* This really must never happen. */
|
||||
|
|
17
nscd/nscd.c
17
nscd/nscd.c
|
@ -79,6 +79,13 @@ time_t start_time;
|
|||
|
||||
uintptr_t pagesize_m1;
|
||||
|
||||
int paranoia;
|
||||
time_t restart_time;
|
||||
time_t restart_interval = RESTART_INTERVAL;
|
||||
const char *oldcwd;
|
||||
uid_t old_uid;
|
||||
gid_t old_gid;
|
||||
|
||||
static int check_pid (const char *file);
|
||||
static int write_pid (const char *file);
|
||||
|
||||
|
@ -248,6 +255,9 @@ main (int argc, char **argv)
|
|||
signal (SIGTTIN, SIG_IGN);
|
||||
signal (SIGTSTP, SIG_IGN);
|
||||
}
|
||||
else
|
||||
/* In foreground mode we are not paranoid. */
|
||||
paranoia = 0;
|
||||
|
||||
/* Start the SELinux AVC. */
|
||||
if (selinux_enabled)
|
||||
|
@ -414,6 +424,7 @@ nscd_open_socket (void)
|
|||
return sock;
|
||||
}
|
||||
|
||||
|
||||
/* Cleanup. */
|
||||
void
|
||||
termination_handler (int signum)
|
||||
|
@ -461,7 +472,11 @@ check_pid (const char *file)
|
|||
n = fscanf (fp, "%d", &pid);
|
||||
fclose (fp);
|
||||
|
||||
if (n != 1 || kill (pid, 0) == 0)
|
||||
/* If we cannot parse the file default to assuming nscd runs.
|
||||
If the PID is alive, assume it is running. That all unless
|
||||
the PID is the same as the current process' since tha latter
|
||||
can mean we re-exec. */
|
||||
if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
# server-user is ignored if nscd is started with -S parameters
|
||||
# stat-user <user who is allowed to request statistics>
|
||||
# reload-count unlimited|<number>
|
||||
# paranoia <yes|no>
|
||||
# restart-interval <time in seconds>
|
||||
#
|
||||
# enable-cache <service> <yes|no>
|
||||
# positive-time-to-live <service> <time in seconds>
|
||||
|
@ -31,6 +33,8 @@
|
|||
# stat-user somebody
|
||||
debug-level 0
|
||||
# reload-count 5
|
||||
paranoia no
|
||||
# restart-interval 3600
|
||||
|
||||
enable-cache passwd yes
|
||||
positive-time-to-live passwd 600
|
||||
|
|
17
nscd/nscd.h
17
nscd/nscd.h
|
@ -50,6 +50,10 @@ typedef enum
|
|||
#define DEFAULT_RELOAD_LIMIT 5
|
||||
|
||||
|
||||
/* Time before restarting the process in paranoia mode. */
|
||||
#define RESTART_INTERVAL (60 * 60)
|
||||
|
||||
|
||||
/* Structure describing dynamic part of one database. */
|
||||
struct database_dyn
|
||||
{
|
||||
|
@ -127,6 +131,19 @@ extern unsigned int reload_count;
|
|||
/* Pagesize minus one. */
|
||||
extern uintptr_t pagesize_m1;
|
||||
|
||||
/* Nonzero if paranoia mode is enabled. */
|
||||
extern int paranoia;
|
||||
/* Time after which the process restarts. */
|
||||
extern time_t restart_time;
|
||||
/* How much time between restarts. */
|
||||
extern time_t restart_interval;
|
||||
/* Old current working directory. */
|
||||
extern const char *oldcwd;
|
||||
/* Old user and group ID. */
|
||||
extern uid_t old_uid;
|
||||
extern gid_t old_gid;
|
||||
|
||||
|
||||
/* Prototypes for global functions. */
|
||||
|
||||
/* nscd.c */
|
||||
|
|
|
@ -18,13 +18,15 @@
|
|||
02111-1307 USA. */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <libintl.h>
|
||||
#include <malloc.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio_ext.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libintl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
@ -191,7 +193,7 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
|
|||
}
|
||||
else if (strcmp (entry, "stat-user") == 0)
|
||||
{
|
||||
if (!arg1)
|
||||
if (arg1 == NULL)
|
||||
dbg_log (_("Must specify user name for stat-user option"));
|
||||
else
|
||||
{
|
||||
|
@ -245,11 +247,41 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
|
|||
dbg_log (_("invalid value for 'reload-count': %u"), count);
|
||||
}
|
||||
}
|
||||
else if (strcmp (entry, "paranoia") == 0)
|
||||
{
|
||||
if (strcmp (arg1, "no") == 0)
|
||||
paranoia = 0;
|
||||
else if (strcmp (arg1, "yes") == 0)
|
||||
paranoia = 1;
|
||||
}
|
||||
else if (strcmp (entry, "restart-interval") == 0)
|
||||
{
|
||||
if (arg1 != NULL)
|
||||
restart_interval = atol (arg1);
|
||||
else
|
||||
dbg_log (_("Must specify value for restart-interval option"));
|
||||
}
|
||||
else
|
||||
dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2);
|
||||
}
|
||||
while (!feof_unlocked (fp));
|
||||
|
||||
if (paranoia)
|
||||
{
|
||||
restart_time = time (NULL) + restart_interval;
|
||||
|
||||
/* Save the old current workding directory if we are in paranoia
|
||||
mode. We have to change back to it. */
|
||||
oldcwd = get_current_dir_name ();
|
||||
if (oldcwd == NULL)
|
||||
{
|
||||
dbg_log (_("\
|
||||
cannot get current working directory: %s; disabling paranoia mode"),
|
||||
strerror (errno));
|
||||
paranoia = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the buffer. */
|
||||
free (line);
|
||||
/* Close configuration file. */
|
||||
|
|
|
@ -143,6 +143,8 @@ receive_print_stats (void)
|
|||
int fd;
|
||||
int i;
|
||||
uid_t uid = getuid ();
|
||||
const char *yesstr = _(" yes");
|
||||
const char *nostr = _(" no");
|
||||
|
||||
/* Find out whether there is another user but root allowed to
|
||||
request statistics. */
|
||||
|
@ -223,8 +225,11 @@ receive_print_stats (void)
|
|||
else
|
||||
printf (_(" %2lus server runtime\n"), diff);
|
||||
|
||||
printf (_("%15lu number of times clients had to wait\n"),
|
||||
data.client_queued);
|
||||
printf (_("%15lu number of times clients had to wait\n"
|
||||
"%15s paranoia mode enabled\n"
|
||||
"%15lu restart internal\n"),
|
||||
data.client_queued, paranoia ? yesstr : nostr,
|
||||
(unsigned long int) restart_interval);
|
||||
|
||||
for (i = 0; i < lastdb; ++i)
|
||||
{
|
||||
|
@ -241,13 +246,13 @@ receive_print_stats (void)
|
|||
/* The locale does not provide this information so we have to
|
||||
translate it ourself. Since we should avoid short translation
|
||||
terms we artifically increase the length. */
|
||||
enabled = data.dbs[i].enabled ? _(" yes") : _(" no");
|
||||
enabled = data.dbs[i].enabled ? yesstr : nostr;
|
||||
if (check_file[0] == '\0')
|
||||
check_file = data.dbs[i].check_file ? _(" yes") : _(" no");
|
||||
check_file = data.dbs[i].check_file ? yesstr : nostr;
|
||||
if (shared[0] == '\0')
|
||||
shared = data.dbs[i].shared ? _(" yes") : _(" no");
|
||||
shared = data.dbs[i].shared ? yesstr : nostr;
|
||||
if (persistent[0] == '\0')
|
||||
persistent = data.dbs[i].persistent ? _(" yes") : _(" no");
|
||||
persistent = data.dbs[i].persistent ? yesstr : nostr;
|
||||
|
||||
if (all == 0)
|
||||
/* If nothing happened so far report a 0% hit rate. */
|
||||
|
|
Loading…
Reference in New Issue