File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / src / preserve_fds.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:55 2014 UTC (10 years ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_10p3_0, v1_8_10p3, HEAD
sudo v 1.8.10p3

    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>