]> granicus.if.org Git - sudo/commitdiff
Redo preserve_fds support to remap high fds so we can get the most
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 24 Dec 2013 22:01:00 +0000 (15:01 -0700)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 24 Dec 2013 22:01:00 +0000 (15:01 -0700)
out of closefrom().  The fds are then restored after closefrom().

src/preserve_fds.c
src/sudo.c
src/sudo.h

index bfecd992ab46d43e9d9057f700601786114745cd..cc2b011448bf80db2b212c2d53be4d1c51565007 100644 (file)
 
 #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>
 /*
  * 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;
index 6be04a96cb4c09d1e3678ef9c013689f00b7fb33..2db2a5e669cfae5e00dfc930a69814c524738f03 100644 (file)
@@ -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]) { \
index c997635313f0d1c80d1db90d3bacd759a3f4dc3f..b123e2d59f99a29b6d33a8223cbe0550aef1cf3d 100644 (file)
@@ -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 */