Annotation of embedaddon/sudo/src/preserve_fds.c, revision 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>