summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTodd C. Miller <Todd.Miller@courtesan.com>2013-12-24 15:01:00 -0700
committerTodd C. Miller <Todd.Miller@courtesan.com>2013-12-24 15:01:00 -0700
commitf5d5eb02bdb67c3203a0788559aca9dc54b5e4cc (patch)
tree5e9f075e70fae9140ff15a501958f1a5c8ec9dbb /src
parent65ed378e9110a8c232d2393b44be3c39857bbe6c (diff)
downloadsudo-f5d5eb02bdb67c3203a0788559aca9dc54b5e4cc.tar.gz
Redo preserve_fds support to remap high fds so we can get the most
out of closefrom(). The fds are then restored after closefrom().
Diffstat (limited to 'src')
-rw-r--r--src/preserve_fds.c152
-rw-r--r--src/sudo.c2
-rw-r--r--src/sudo.h17
3 files changed, 132 insertions, 39 deletions
diff --git a/src/preserve_fds.c b/src/preserve_fds.c
index bfecd992a..cc2b01144 100644
--- a/src/preserve_fds.c
+++ b/src/preserve_fds.c
@@ -16,13 +16,7 @@
#include <config.h>
-#include <sys/param.h> /* for howmany() on Linux */
-#ifdef HAVE_SYS_SYSMACROS_H
-# include <sys/sysmacros.h> /* for howmany() on Solaris */
-#endif
-#ifdef HAVE_SYS_SELECT_H
-# include <sys/select.h> /* for FD_* macros */
-#endif /* HAVE_SYS_SELECT_H */
+#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
@@ -53,21 +47,62 @@
/*
* Add an fd to preserve.
*/
-void
-add_preserved_fd(struct preserved_fds *pfds, int fd)
+int
+add_preserved_fd(struct preserved_fd_list *pfds, int fd)
{
+ struct preserved_fd *pfd, *pfd_new;
debug_decl(add_preserved_fd, SUDO_DEBUG_UTIL)
- /* Reallocate as needed before adding. */
- if (fd > pfds->maxfd) {
- int osize = howmany(pfds->maxfd + 1, NFDBITS);
- int nsize = howmany(fd + 1, NFDBITS);
- if (nsize > osize)
- pfds->fds = erecalloc(pfds->fds, osize, nsize, sizeof(fd_mask));
- pfds->maxfd = fd;
+ pfd_new = emalloc(sizeof(*pfd));
+ pfd_new->lowfd = fd;
+ pfd_new->highfd = fd;
+ pfd_new->flags = fcntl(fd, F_GETFD);
+ if (pfd_new->flags == -1) {
+ efree(pfd_new);
+ debug_return_int(-1);
+ }
+
+ TAILQ_FOREACH(pfd, pfds, entries) {
+ if (fd == pfd->highfd) {
+ /* already preserved */
+ efree(pfd_new);
+ break;
+ }
+ if (fd < pfd->highfd) {
+ TAILQ_INSERT_BEFORE(pfd, pfd_new, entries);
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "preserving fd %d", fd);
+ break;
+ }
}
- FD_SET(fd, pfds->fds);
+ if (pfd == NULL) {
+ TAILQ_INSERT_TAIL(pfds, pfd_new, entries);
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "preserving fd %d", fd);
+ }
+
+ debug_return_int(0);
+}
+
+/*
+ * Close fds in the range [from,to]
+ */
+static void
+closefrom_range(int from, int to)
+{
+ debug_decl(closefrom_range, SUDO_DEBUG_UTIL)
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "closing fds [%d, %d]", from, to);
+ while (from <= to) {
+#ifdef __APPLE__
+ /* Avoid potential libdispatch crash when we close its fds. */
+ (void) fcntl(from, F_SETFD, FD_CLOEXEC);
+#else
+ (void) close(from);
+#endif
+ from++;
+ }
debug_return;
}
@@ -76,27 +111,82 @@ add_preserved_fd(struct preserved_fds *pfds, int fd)
* in pfds.
*/
void
-closefrom_except(int startfd, struct preserved_fds *pfds)
+closefrom_except(int startfd, struct preserved_fd_list *pfds)
{
- int fd;
+ int tmpfd;
+ struct preserved_fd *pfd, *pfd_next;
debug_decl(closefrom_except, SUDO_DEBUG_UTIL)
- /* First handle the preserved fds. */
- if (startfd <= pfds->maxfd) {
- for (fd = startfd; fd <= pfds->maxfd; fd++) {
- if (!FD_ISSET(fd, pfds->fds)) {
-#ifdef __APPLE__
- /* Avoid potential libdispatch crash when we close its fds. */
- (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
-#else
- (void) close(fd);
-#endif
+ /*
+ * First, relocate preserved fds to be as contiguous as possible.
+ */
+ TAILQ_FOREACH_SAFE(pfd, pfds, entries, pfd_next) {
+ if (pfd->highfd < startfd)
+ continue;
+ tmpfd = dup(pfd->highfd);
+ if (tmpfd < pfd->highfd) {
+ if (tmpfd == -1) {
+ if (errno == EBADF)
+ TAILQ_REMOVE(pfds, pfd, entries);
+ continue;
}
+ pfd->lowfd = tmpfd;
+ tmpfd = pfd->highfd;
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "dup %d -> %d", pfd->highfd, pfd->lowfd);
}
- startfd = pfds->maxfd + 1;
+ (void) close(tmpfd);
+ }
+
+ if (TAILQ_EMPTY(pfds)) {
+ /* No fds to preserve. */
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "closefrom(%d)", startfd);
+ closefrom(startfd);
+ debug_return;
}
+
+ /* Close any fds [startfd,TAILQ_FIRST(pfds)->lowfd) */
+ closefrom_range(startfd, TAILQ_FIRST(pfds)->lowfd - 1);
+
+ /* Close any unpreserved fds (TAILQ_LAST(pfds)->lowfd,startfd) */
+ TAILQ_FOREACH_SAFE(pfd, pfds, entries, pfd_next) {
+ if (pfd->lowfd < startfd)
+ continue;
+ if (pfd_next != NULL && pfd->lowfd + 1 != pfd_next->lowfd)
+ closefrom_range(pfd->lowfd + 1, pfd_next->lowfd);
+ }
+
+ /* Let closefrom() do the rest for us. */
+ pfd = TAILQ_LAST(pfds, preserved_fd_list);
+ if (pfd != NULL && pfd->lowfd + 1 > startfd)
+ startfd = pfd->lowfd + 1;
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "closefrom(%d)", startfd);
closefrom(startfd);
+ /* Restore preserved fds and set flags. */
+ TAILQ_FOREACH(pfd, pfds, entries) {
+ if (pfd->lowfd != pfd->highfd) {
+ if (dup2(pfd->lowfd, pfd->highfd) == -1) {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "dup2(%d, %d): %s", pfd->lowfd, pfd->highfd,
+ strerror(errno));
+ } else {
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "dup2(%d, %d)", pfd->lowfd, pfd->highfd);
+ }
+ if (fcntl(pfd->highfd, F_SETFL, pfd->flags) == -1) {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "fcntl(%d, F_SETFL, %d): %s", pfd->highfd,
+ pfd->flags, strerror(errno));
+ } else {
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "fcntl(%d, F_SETFL, %d)", pfd->highfd, pfd->flags);
+ }
+ (void) close(pfd->lowfd);
+ }
+ }
debug_return;
}
@@ -104,7 +194,7 @@ closefrom_except(int startfd, struct preserved_fds *pfds)
* Parse a comma-separated list of fds and add them to preserved_fds.
*/
void
-parse_preserved_fds(struct preserved_fds *pfds, const char *fdstr)
+parse_preserved_fds(struct preserved_fd_list *pfds, const char *fdstr)
{
const char *cp = fdstr;
long lval;
diff --git a/src/sudo.c b/src/sudo.c
index 6be04a96c..2db2a5e66 100644
--- a/src/sudo.c
+++ b/src/sudo.c
@@ -536,7 +536,7 @@ command_info_to_details(char * const info[], struct command_details *details)
memset(details, 0, sizeof(*details));
details->closefrom = -1;
- details->preserved_fds.maxfd = -1;
+ TAILQ_INIT(&details->preserved_fds);
#define SET_STRING(s, n) \
if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
diff --git a/src/sudo.h b/src/sudo.h
index c99763531..b123e2d59 100644
--- a/src/sudo.h
+++ b/src/sudo.h
@@ -122,10 +122,13 @@ struct user_details {
#define CD_SET_UTMP 0x2000
#define CD_EXEC_BG 0x4000
-struct preserved_fds {
- int maxfd;
- fd_set *fds;
+struct preserved_fd {
+ TAILQ_ENTRY(preserved_fd) entries;
+ int lowfd;
+ int highfd;
+ int flags;
};
+TAILQ_HEAD(preserved_fd_list, preserved_fd);
struct command_details {
uid_t uid;
@@ -138,7 +141,7 @@ struct command_details {
int ngroups;
int closefrom;
int flags;
- struct preserved_fds preserved_fds;
+ struct preserved_fd_list preserved_fds;
struct passwd *pw;
GETGROUPS_T *groups;
const char *command;
@@ -247,8 +250,8 @@ void save_signals(void);
void preload_static_symbols(void);
/* preserve_fds.c */
-void add_preserved_fd(struct preserved_fds *pfds, int fd);
-void closefrom_except(int startfd, struct preserved_fds *pfds);
-void parse_preserved_fds(struct preserved_fds *pfds, const char *fdstr);
+int add_preserved_fd(struct preserved_fd_list *pfds, int fd);
+void closefrom_except(int startfd, struct preserved_fd_list *pfds);
+void parse_preserved_fds(struct preserved_fd_list *pfds, const char *fdstr);
#endif /* _SUDO_SUDO_H */