summaryrefslogtreecommitdiff
path: root/nscd/connections.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2004-10-03 19:33:48 +0000
committerUlrich Drepper <drepper@redhat.com>2004-10-03 19:33:48 +0000
commit4401d759051714fcc016a146685f3c13bed49442 (patch)
treefb2a02b91616e460a2b1fcd6eb482a0b3db9d860 /nscd/connections.c
parentfc03df7aa6d9de00d09ddaf9c27074fb4ea6d3ef (diff)
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.
Diffstat (limited to 'nscd/connections.c')
-rw-r--r--nscd/connections.c189
1 files changed, 184 insertions, 5 deletions
diff --git a/nscd/connections.c b/nscd/connections.c
index 8b167aab71..ace69fb455 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -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. */