Add support for preventing fds from getting clobbered by closefrom().
This commit is contained in:
1
MANIFEST
1
MANIFEST
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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@),
|
||||
|
@@ -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@ ,
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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 \
|
||||
|
25
src/exec.c
25
src/exec.c
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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
132
src/preserve_fds.c
Normal 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;
|
||||
}
|
@@ -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) {
|
||||
|
11
src/sudo.h
11
src/sudo.h
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user