Add support for preventing fds from getting clobbered by closefrom().

This commit is contained in:
Todd C. Miller
2013-12-20 11:14:32 -07:00
parent 388ad69f09
commit 1adeda54ef
14 changed files with 218 additions and 35 deletions

View File

@@ -407,6 +407,7 @@ src/locale_stub.c
src/net_ifs.c
src/openbsd.c
src/parse_args.c
src/preserve_fds.c
src/po/README
src/po/cs.mo
src/po/cs.po

View File

@@ -563,18 +563,10 @@ sudo_debug_execve2(int level, const char *path, char *const argv[], char *const
}
/*
* Dup sudo_debug_fd to the specified value so we don't
* close it when calling closefrom().
* Getter for the debug descriptor.
*/
int
sudo_debug_fd_set(int fd)
sudo_debug_fd_get(void)
{
if (sudo_debug_fd != -1 && fd != sudo_debug_fd) {
if (dup2(sudo_debug_fd, fd) == -1)
return -1;
(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
close(sudo_debug_fd);
sudo_debug_fd = fd;
}
return sudo_debug_fd;
}

View File

@@ -548,6 +548,11 @@ DDEESSCCRRIIPPTTIIOONN
If set, prevent the command from executing other
programs.
preserve_fds=list
A comma-separated list of file descriptors that should
be preserved, regardless of the value of the _c_l_o_s_e_f_r_o_m
setting. Only available starting with API version 1.5.
preserve_groups=bool
If set, ssuuddoo will preserve the user's group vector
instead of initializing the group vector based on
@@ -1437,6 +1442,9 @@ PPLLUUGGIINN AAPPII CCHHAANNGGEELLOOGG
Version 1.4 (sudo 1.8.8)
The _r_e_m_o_t_e___h_o_s_t entry was added to the settings list.
Version 1.5 (sudo 1.8.9)
The entry was added to the command_info list.
SSEEEE AALLSSOO
sudo.conf(4), sudoers(4), sudo(1m)
@@ -1456,4 +1464,4 @@ DDIISSCCLLAAIIMMEERR
file distributed with ssuuddoo or http://www.sudo.ws/sudo/license.html for
complete details.
Sudo 1.8.9 December 4, 2013 Sudo 1.8.9
Sudo 1.8.9 December 20, 2013 Sudo 1.8.9

View File

@@ -16,7 +16,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.TH "SUDO_PLUGIN" "5" "December 4, 2013" "Sudo @PACKAGE_VERSION@" "OpenBSD Programmer's Manual"
.TH "SUDO_PLUGIN" "5" "December 20, 2013" "Sudo @PACKAGE_VERSION@" "OpenBSD Programmer's Manual"
.nh
.if n .ad l
.SH "NAME"
@@ -914,6 +914,13 @@ on BSD systems.
noexec=bool
If set, prevent the command from executing other programs.
.TP 6n
preserve_fds=list
A comma-separated list of file descriptors that should be
preserved, regardless of the value of the
\fIclosefrom\fR
setting.
Only available starting with API version 1.5.
.TP 6n
preserve_groups=bool
If set,
\fBsudo\fR
@@ -2599,6 +2606,12 @@ The
entry was added to the
\fRsettings\fR
list.
.TP 6n
Version 1.5 (sudo 1.8.9)
The
entry was added to the
\fRcommand_info\fR
list.
.SH "SEE ALSO"
sudo.conf(@mansectform@),
sudoers(@mansectform@),

View File

@@ -14,7 +14,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd December 4, 2013
.Dd December 20, 2013
.Dt SUDO_PLUGIN @mansectform@
.Os Sudo @PACKAGE_VERSION@
.Sh NAME
@@ -808,6 +808,12 @@ The nice value, if specified, overrides the priority associated with the
on BSD systems.
.It noexec=bool
If set, prevent the command from executing other programs.
.It preserve_fds=list
A comma-separated list of file descriptors that should be
preserved, regardless of the value of the
.Em closefrom
setting.
Only available starting with API version 1.5.
.It preserve_groups=bool
If set,
.Nm sudo
@@ -2253,6 +2259,12 @@ The
entry was added to the
.Li settings
list.
.It Version 1.5 (sudo 1.8.9)
The
.em preserve_fds
entry was added to the
.Li command_info
list.
.El
.Sh SEE ALSO
.Xr sudo.conf @mansectform@ ,

View File

@@ -218,7 +218,7 @@ void sudo_debug_exit_bool(const char *func, const char *file, int line, int subs
void sudo_debug_exit_str(const char *func, const char *file, int line, int subsys, const char *rval);
void sudo_debug_exit_str_masked(const char *func, const char *file, int line, int subsys, const char *rval);
void sudo_debug_exit_ptr(const char *func, const char *file, int line, int subsys, const void *rval);
int sudo_debug_fd_set(int fd);
int sudo_debug_fd_get(void);
int sudo_debug_init(const char *debugfile, const char *settings);
void sudo_debug_printf_nvm(int pri, const char *fmt, ...) __printf0like(2, 3);
void sudo_debug_printf2(const char *func, const char *file, int line, int level, const char *fmt, ...) __printf0like(5, 6);

View File

@@ -19,7 +19,7 @@
/* API version major/minor */
#define SUDO_API_VERSION_MAJOR 1
#define SUDO_API_VERSION_MINOR 4
#define SUDO_API_VERSION_MINOR 5
#define SUDO_API_MKVERSION(x, y) ((x << 16) | y)
#define SUDO_API_VERSION SUDO_API_MKVERSION(SUDO_API_VERSION_MAJOR, SUDO_API_VERSION_MINOR)

View File

@@ -90,8 +90,9 @@ SHELL = @SHELL@
PROGS = @PROGS@
OBJS = conversation.o env_hooks.o exec.o exec_common.o exec_pty.o \
get_pty.o hooks.o net_ifs.o load_plugins.o parse_args.o signal.o \
sudo.o sudo_edit.o tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
get_pty.o hooks.o net_ifs.o load_plugins.o parse_args.o \
preserve_fds.o signal.o sudo.o sudo_edit.o tgetpass.o ttyname.o \
utmp.o @SUDO_OBJS@
SESH_OBJS = sesh.o locale_stub.o exec_common.o
@@ -179,7 +180,8 @@ cleandir: realclean
# Autogenerated dependencies, do not modify
check_ttyname.o: $(srcdir)/regress/ttyname/check_ttyname.c $(incdir)/alloc.h \
$(incdir)/fatal.h $(incdir)/missing.h $(top_builddir)/config.h
$(incdir)/fatal.h $(incdir)/missing.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h $(top_srcdir)/compat/stdbool.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/ttyname/check_ttyname.c
conversation.o: $(srcdir)/conversation.c $(incdir)/alloc.h $(incdir)/fatal.h \
$(incdir)/fileops.h $(incdir)/gettext.h $(incdir)/missing.h \
@@ -263,6 +265,13 @@ parse_args.o: $(srcdir)/parse_args.c $(incdir)/alloc.h $(incdir)/fatal.h \
preload.o: $(srcdir)/preload.c $(incdir)/sudo_dso.h $(incdir)/sudo_plugin.h \
$(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/preload.c
preserve_fds.o: $(srcdir)/preserve_fds.c $(incdir)/alloc.h $(incdir)/fatal.h \
$(incdir)/fileops.h $(incdir)/gettext.h $(incdir)/missing.h \
$(incdir)/queue.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_util.h $(srcdir)/sudo.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h \
$(top_srcdir)/compat/stdbool.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/preserve_fds.c
selinux.o: $(srcdir)/selinux.c $(incdir)/alloc.h $(incdir)/fatal.h \
$(incdir)/fileops.h $(incdir)/gettext.h $(incdir)/missing.h \
$(incdir)/queue.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \

View File

@@ -143,7 +143,7 @@ static int fork_cmnd(struct command_details *details, int sv[2])
close(signal_pipe[0]);
close(signal_pipe[1]);
fcntl(sv[1], F_SETFD, FD_CLOEXEC);
exec_cmnd(details, &cstat, &sv[1]);
exec_cmnd(details, &cstat, sv[1]);
send(sv[1], &cstat, sizeof(cstat), 0);
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, 1);
_exit(1);
@@ -161,7 +161,7 @@ static int fork_cmnd(struct command_details *details, int sv[2])
*/
void
exec_cmnd(struct command_details *details, struct command_status *cstat,
int *errfd)
int errfd)
{
debug_decl(exec_cmnd, SUDO_DEBUG_EXEC)
@@ -171,16 +171,15 @@ exec_cmnd(struct command_details *details, struct command_status *cstat,
sudo_debug_execve(SUDO_DEBUG_INFO, details->command,
details->argv, details->envp);
if (details->closefrom >= 0) {
int maxfd = details->closefrom;
/* Preserve back channel if present. */
if (errfd != NULL) {
dup2(*errfd, maxfd);
(void)fcntl(maxfd, F_SETFD, FD_CLOEXEC);
*errfd = maxfd++;
}
if (sudo_debug_fd_set(maxfd) != -1)
maxfd++;
closefrom(maxfd);
/* Preserve debug fd and error pipe as needed. */
int debug_fd = sudo_debug_fd_get();
if (debug_fd != -1)
add_preserved_fd(&details->preserved_fds, debug_fd);
if (errfd != -1)
add_preserved_fd(&details->preserved_fds, errfd);
/* Close all fds except those explicitly preserved. */
closefrom_except(details->closefrom, &details->preserved_fds);
}
#ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
@@ -382,7 +381,7 @@ sudo_execute(struct command_details *details, struct command_status *cstat)
} else if (!ISSET(details->flags, CD_SET_TIMEOUT) &&
policy_plugin.u.policy->close == NULL) {
/* If no I/O logging, timeout or policy close we can exec directly. */
exec_cmnd(details, cstat, NULL);
exec_cmnd(details, cstat, -1);
goto done;
}

View File

@@ -99,7 +99,7 @@ static struct io_buffer_list iobufs;
static void del_io_events(void);
static int exec_monitor(struct command_details *details, int backchannel);
static void exec_pty(struct command_details *details,
struct command_status *cstat, int *errfd);
struct command_status *cstat, int errfd);
static void sigwinch(int s);
static void sync_ttysize(int src, int dst);
static void deliver_signal(pid_t pid, int signo, bool from_parent);
@@ -1282,7 +1282,7 @@ exec_monitor(struct command_details *details, int backchannel)
restore_signals();
/* setup tty and exec command */
exec_pty(details, &cstat, &errpipe[1]);
exec_pty(details, &cstat, errpipe[1]);
ignore_result(write(errpipe[1], &cstat, sizeof(cstat)));
_exit(1);
}
@@ -1375,7 +1375,7 @@ bad:
*/
static void
exec_pty(struct command_details *details,
struct command_status *cstat, int *errfd)
struct command_status *cstat, int errfd)
{
pid_t self = getpid();
debug_decl(exec_pty, SUDO_DEBUG_EXEC);

132
src/preserve_fds.c Normal file
View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2013 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#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 <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include "sudo.h"
/*
* Add an fd to preserve.
*/
void
add_preserved_fd(struct preserved_fds *pfds, int fd)
{
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;
}
FD_SET(fd, pfds->fds);
debug_return;
}
/*
* Close all descriptors, startfd and higher except those listed
* in pfds.
*/
void
closefrom_except(int startfd, struct preserved_fds *pfds)
{
int fd;
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
}
}
startfd = pfds->maxfd + 1;
}
closefrom(startfd);
debug_return;
}
/*
* Parse a comma-separated list of fds and add them to preserved_fds.
*/
void
parse_preserved_fds(struct preserved_fds *pfds, const char *fdstr)
{
const char *cp = fdstr;
long lval;
char *ep;
debug_decl(parse_preserved_fds, SUDO_DEBUG_UTIL)
do {
errno = 0;
lval = strtol(cp, &ep, 10);
if (ep == cp || (*ep != ',' && *ep != '\0')) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to parse fd string %s", cp);
break;
}
if ((errno == ERANGE && lval == LONG_MAX) || lval < 0 || lval > INT_MAX) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"range error parsing fd string %s", cp);
} else {
add_preserved_fd(pfds, (int)lval);
}
cp = ep + 1;
} while (*ep != '\0');
debug_return;
}

View File

@@ -536,6 +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;
#define SET_STRING(s, n) \
if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
@@ -590,6 +591,11 @@ command_info_to_details(char * const info[], struct command_details *details)
SET(details->flags, CD_PRESERVE_GROUPS);
break;
}
if (strncmp("preserve_fds=", info[i], sizeof("preserve_fds=") - 1) == 0) {
parse_preserved_fds(&details->preserved_fds,
info[i] + sizeof("preserve_fds=") - 1);
break;
}
break;
case 'r':
if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {

View File

@@ -122,6 +122,11 @@ struct user_details {
#define CD_SET_UTMP 0x2000
#define CD_EXEC_BG 0x4000
struct preserved_fds {
int maxfd;
fd_set *fds;
};
struct command_details {
uid_t uid;
uid_t euid;
@@ -133,6 +138,7 @@ struct command_details {
int ngroups;
int closefrom;
int flags;
struct preserved_fds preserved_fds;
struct passwd *pw;
GETGROUPS_T *groups;
const char *command;
@@ -240,4 +246,9 @@ void save_signals(void);
/* preload.c */
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);
#endif /* _SUDO_SUDO_H */

View File

@@ -62,7 +62,7 @@ struct command_status;
int fork_pty(struct command_details *details, int sv[], sigset_t *omask);
int suspend_parent(int signo);
void exec_cmnd(struct command_details *details, struct command_status *cstat,
int *errfd);
int errfd);
void add_io_events(struct sudo_event_base *evbase);
#ifdef SA_SIGINFO
void handler(int s, siginfo_t *info, void *context);