Annotation of embedaddon/sudo/src/preserve_fds.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (c) 2013-2014 Todd C. Miller <Todd.Miller@courtesan.com>
                      3:  *
                      4:  * Permission to use, copy, modify, and distribute this software for any
                      5:  * purpose with or without fee is hereby granted, provided that the above
                      6:  * copyright notice and this permission notice appear in all copies.
                      7:  *
                      8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                      9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     15:  */
                     16: 
                     17: #include <config.h>
                     18: 
                     19: #include <sys/param.h>         /* for howmany() on Linux */
                     20: #ifdef HAVE_SYS_SYSMACROS_H
                     21: # include <sys/sysmacros.h>    /* for howmany() on Solaris */
                     22: #endif /* HAVE_SYS_SYSMACROS_H */
                     23: #ifdef HAVE_SYS_SELECT_H
                     24: # include <sys/select.h>       /* for FD_* macros */
                     25: #endif /* HAVE_SYS_SELECT_H */
                     26: #include <stdio.h>
                     27: #ifdef STDC_HEADERS
                     28: # include <stdlib.h>
                     29: # include <stddef.h>
                     30: #else
                     31: # ifdef HAVE_STDLIB_H
                     32: #  include <stdlib.h>
                     33: # endif
                     34: #endif /* STDC_HEADERS */
                     35: #ifdef HAVE_STRING_H
                     36: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
                     37: #  include <memory.h>
                     38: # endif
                     39: # include <string.h>
                     40: #endif /* HAVE_STRING_H */
                     41: #ifdef HAVE_STRINGS_H
                     42: # include <strings.h>
                     43: #endif /* HAVE_STRINGS_H */
                     44: #ifdef HAVE_UNISTD_H
                     45: # include <unistd.h>
                     46: #endif /* HAVE_UNISTD_H */
                     47: #include <errno.h>
                     48: #include <fcntl.h>
                     49: #include <limits.h>
                     50: 
                     51: #include "sudo.h"
                     52: 
                     53: /*
                     54:  * Add an fd to preserve.
                     55:  */
                     56: int
                     57: add_preserved_fd(struct preserved_fd_list *pfds, int fd)
                     58: {
                     59:     struct preserved_fd *pfd, *pfd_new;
                     60:     debug_decl(add_preserved_fd, SUDO_DEBUG_UTIL)
                     61: 
                     62:     pfd_new = emalloc(sizeof(*pfd));
                     63:     pfd_new->lowfd = fd;
                     64:     pfd_new->highfd = fd;
                     65:     pfd_new->flags = fcntl(fd, F_GETFD);
                     66:     if (pfd_new->flags == -1) {
                     67:        efree(pfd_new);
                     68:        debug_return_int(-1);
                     69:     }
                     70: 
                     71:     TAILQ_FOREACH(pfd, pfds, entries) {
                     72:        if (fd == pfd->highfd) {
                     73:            /* already preserved */
                     74:            sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                     75:                "fd %d already preserved", fd);
                     76:            efree(pfd_new);
                     77:            break;
                     78:        }
                     79:        if (fd < pfd->highfd) {
                     80:            TAILQ_INSERT_BEFORE(pfd, pfd_new, entries);
                     81:            sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                     82:                "preserving fd %d", fd);
                     83:            break;
                     84:        }
                     85:     }
                     86:     if (pfd == NULL) {
                     87:        TAILQ_INSERT_TAIL(pfds, pfd_new, entries);
                     88:        sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                     89:            "preserving fd %d", fd);
                     90:     }
                     91: 
                     92:     debug_return_int(0);
                     93: }
                     94: 
                     95: /*
                     96:  * Close all descriptors, startfd and higher except those listed
                     97:  * in pfds.
                     98:  */
                     99: void
                    100: closefrom_except(int startfd, struct preserved_fd_list *pfds)
                    101: {
                    102:     int debug_fd, fd, lastfd = -1;
                    103:     struct preserved_fd *pfd, *pfd_next;
                    104:     fd_set *fdsp;
                    105:     debug_decl(closefrom_except, SUDO_DEBUG_UTIL)
                    106: 
                    107:     debug_fd = sudo_debug_fd_get();
                    108: 
                    109:     /* First, relocate preserved fds to be as contiguous as possible.  */
                    110:     TAILQ_FOREACH_REVERSE_SAFE(pfd, pfds, preserved_fd_list, entries, pfd_next) {
                    111:        if (pfd->highfd < startfd)
                    112:            continue;
                    113:        fd = dup(pfd->highfd);
                    114:        if (fd == -1) {
                    115:            sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
                    116:                "dup %d", pfd->highfd);
                    117:            if (errno == EBADF) {
                    118:                TAILQ_REMOVE(pfds, pfd, entries);
                    119:                continue;
                    120:            }
                    121:            /* NOTE: still need to adjust lastfd below with unchanged lowfd. */
                    122:        } else if (fd < pfd->highfd) {
                    123:            pfd->lowfd = fd;
                    124:            fd = pfd->highfd;
                    125:            if (fd == debug_fd)
                    126:                debug_fd = sudo_debug_fd_set(pfd->lowfd);
                    127:            sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                    128:                "dup %d -> %d", pfd->highfd, pfd->lowfd);
                    129:        }
                    130:        if (fd != -1)
                    131:            (void) close(fd);
                    132: 
                    133:        if (pfd->lowfd > lastfd)
                    134:            lastfd = pfd->lowfd;        /* highest (relocated) preserved fd */
                    135:     }
                    136: 
                    137:     if (lastfd == -1) {
                    138:        /* No fds to preserve. */
                    139:        sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                    140:            "closefrom(%d)", startfd);
                    141:        closefrom(startfd);
                    142:        debug_return;
                    143:     }
                    144: 
                    145:     /* Create bitmap of preserved (relocated) fds.  */
                    146:     fdsp = ecalloc(howmany(lastfd + 1, NFDBITS), sizeof(fd_mask));
                    147:     TAILQ_FOREACH(pfd, pfds, entries) {
                    148:        FD_SET(pfd->lowfd, fdsp);
                    149:     }
                    150: 
                    151:     /*
                    152:      * Close any unpreserved fds [startfd,lastfd]
                    153:      */
                    154:     for (fd = startfd; fd <= lastfd; fd++) {
                    155:        if (!FD_ISSET(fd, fdsp)) {
                    156:            sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                    157:                "closing fd %d", fd);
                    158: #ifdef __APPLE__
                    159:            /* Avoid potential libdispatch crash when we close its fds. */
                    160:            (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
                    161: #else
                    162:            (void) close(fd);
                    163: #endif
                    164:        }
                    165:     }
                    166:     free(fdsp);
                    167: 
                    168:     /* Let closefrom() do the rest for us. */
                    169:     sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                    170:        "closefrom(%d)", lastfd + 1);
                    171:     closefrom(lastfd + 1);
                    172: 
                    173:     /* Restore preserved fds and set flags. */
                    174:     TAILQ_FOREACH_REVERSE(pfd, pfds, preserved_fd_list, entries) {
                    175:        if (pfd->lowfd != pfd->highfd) {
                    176:            if (dup2(pfd->lowfd, pfd->highfd) == -1) {
                    177:                sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
                    178:                    "dup2(%d, %d): %s", pfd->lowfd, pfd->highfd,
                    179:                    strerror(errno));
                    180:            } else {
                    181:                sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                    182:                    "dup2(%d, %d)", pfd->lowfd, pfd->highfd);
                    183:            }
                    184:            if (fcntl(pfd->highfd, F_SETFD, pfd->flags) == -1) {
                    185:                sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
                    186:                    "fcntl(%d, F_SETFD, %d): %s", pfd->highfd,
                    187:                    pfd->flags, strerror(errno));
                    188:            } else {
                    189:                sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                    190:                    "fcntl(%d, F_SETFD, %d)", pfd->highfd, pfd->flags);
                    191:            }
                    192:            if (pfd->lowfd == debug_fd)
                    193:                debug_fd = sudo_debug_fd_set(pfd->highfd);
                    194:            (void) close(pfd->lowfd);
                    195:            pfd->lowfd = pfd->highfd;
                    196:        }
                    197:     }
                    198:     debug_return;
                    199: }
                    200: 
                    201: /*
                    202:  * Parse a comma-separated list of fds and add them to preserved_fds.
                    203:  */
                    204: void
                    205: parse_preserved_fds(struct preserved_fd_list *pfds, const char *fdstr)
                    206: {
                    207:     const char *cp = fdstr;
                    208:     long lval;
                    209:     char *ep;
                    210:     debug_decl(parse_preserved_fds, SUDO_DEBUG_UTIL)
                    211: 
                    212:     do {
                    213:        errno = 0;
                    214:        lval = strtol(cp, &ep, 10);
                    215:        if (ep == cp || (*ep != ',' && *ep != '\0')) {
                    216:            sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
                    217:                "unable to parse fd string %s", cp);
                    218:            break;
                    219:        }
                    220:        if ((errno == ERANGE && lval == LONG_MAX) || lval < 0 || lval > INT_MAX) {
                    221:            sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
                    222:                "range error parsing fd string %s", cp);
                    223:        } else {
                    224:            add_preserved_fd(pfds, (int)lval);
                    225:        }
                    226:        cp = ep + 1;
                    227:     } while (*ep != '\0');
                    228: 
                    229:     debug_return;
                    230: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>