src/preload.c
src/selinux.c
src/sesh.c
+src/signal.c
src/solaris.c
src/sudo.c
src/sudo.h
/* API version major/minor */
#define SUDO_API_VERSION_MAJOR 1
-#define SUDO_API_VERSION_MINOR 2
+#define SUDO_API_VERSION_MINOR 3
#define SUDO_API_MKVERSION(x, y) ((x << 16) | y)
#define SUDO_API_VERSION SUDO_API_MKVERSION(SUDO_API_VERSION_MAJOR, SUDO_API_VERSION_MINOR)
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 sudo.o sudo_edit.o \
- tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
+ 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@
SESH_OBJS = sesh.o locale_stub.o exec_common.o
$(incdir)/list.h $(incdir)/sudo_debug.h $(srcdir)/sudo_exec.h \
$(incdir)/sudo_plugin.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sesh.c
+signal.o: $(srcdir)/signal.c $(top_builddir)/config.h $(srcdir)/sudo.h \
+ $(top_builddir)/pathnames.h $(top_srcdir)/compat/stdbool.h \
+ $(incdir)/missing.h $(incdir)/alloc.h $(incdir)/error.h \
+ $(incdir)/fileops.h $(incdir)/list.h $(incdir)/sudo_conf.h \
+ $(incdir)/list.h $(incdir)/sudo_debug.h $(incdir)/gettext.h
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/signal.c
solaris.o: $(srcdir)/solaris.c $(top_builddir)/config.h $(srcdir)/sudo.h \
$(top_builddir)/pathnames.h $(top_srcdir)/compat/stdbool.h \
$(incdir)/missing.h $(incdir)/alloc.h $(incdir)/error.h \
* XXX - currently we send SIGCONT upon resume in some cases where
* we don't need to (e.g. command pgrp == parent pgrp).
*/
- zero_bytes(&sa, sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
#ifdef SA_SIGINFO
#else
sa.sa_handler = handler;
#endif
- sigaction(SIGCONT, &sa, NULL);
+ sudo_sigaction(SIGCONT, &sa, NULL, false);
#ifdef SA_SIGINFO
sa.sa_sigaction = handler_user_only;
#endif
- sigaction(SIGTSTP, &sa, NULL);
+ sudo_sigaction(SIGTSTP, &sa, NULL, true);
/*
* The policy plugin's session init must be run before we fork
debug_return_int(cmnd_pid);
}
-static struct signal_state {
- int signo;
- sigaction_t sa;
-} saved_signals[] = {
- { SIGALRM }, /* SAVED_SIGALRM */
- { SIGCHLD }, /* SAVED_SIGCHLD */
- { SIGCONT }, /* SAVED_SIGCONT */
- { SIGHUP }, /* SAVED_SIGHUP */
- { SIGINT }, /* SAVED_SIGINT */
- { SIGPIPE }, /* SAVED_SIGPIPE */
- { SIGQUIT }, /* SAVED_SIGQUIT */
- { SIGTERM }, /* SAVED_SIGTERM */
- { SIGTSTP }, /* SAVED_SIGTSTP */
- { SIGTTIN }, /* SAVED_SIGTTIN */
- { SIGTTOU }, /* SAVED_SIGTTOU */
- { SIGUSR1 }, /* SAVED_SIGUSR1 */
- { SIGUSR2 }, /* SAVED_SIGUSR2 */
- { -1 }
-};
-
-/*
- * Save signal handler state so it can be restored before exec.
- */
-void
-save_signals(void)
-{
- struct signal_state *ss;
- debug_decl(save_signals, SUDO_DEBUG_EXEC)
-
- for (ss = saved_signals; ss->signo != -1; ss++)
- sigaction(ss->signo, NULL, &ss->sa);
-
- debug_return;
-}
-
-/*
- * Restore signal handlers to initial state.
- */
-void
-restore_signals(void)
-{
- struct signal_state *ss;
- debug_decl(restore_signals, SUDO_DEBUG_EXEC)
-
- for (ss = saved_signals; ss->signo != -1; ss++)
- sigaction(ss->signo, &ss->sa, NULL);
-
- debug_return;
-}
-
/*
* Execute a command, potentially in a pty with I/O loggging.
* This is a little bit tricky due to how POSIX job control works and
* We block all other signals while running the signal handler.
* Note: HP-UX select() will not be interrupted if SA_RESTART set.
*/
- zero_bytes(&sa, sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
#ifdef SA_SIGINFO
#else
sa.sa_handler = handler;
#endif
- sigaction(SIGALRM, &sa, NULL);
- sigaction(SIGCHLD, &sa, NULL);
- sigaction(SIGPIPE, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
- sigaction(SIGUSR1, &sa, NULL);
- sigaction(SIGUSR2, &sa, NULL);
+ sudo_sigaction(SIGTERM, &sa, NULL, true);
+ sudo_sigaction(SIGALRM, &sa, NULL, false); /* XXX - only if there is a timeout */
+ sudo_sigaction(SIGCHLD, &sa, NULL, false);
+ sudo_sigaction(SIGPIPE, &sa, NULL, false);
+ sudo_sigaction(SIGUSR1, &sa, NULL, true);
+ sudo_sigaction(SIGUSR2, &sa, NULL, true);
/*
* When not running the command in a pty, we do not want to
sa.sa_sigaction = handler_user_only;
}
#endif
- sigaction(SIGHUP, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGQUIT, &sa, NULL);
+ sudo_sigaction(SIGHUP, &sa, NULL, true);
+ sudo_sigaction(SIGINT, &sa, NULL, true);
+ sudo_sigaction(SIGQUIT, &sa, NULL, true);
/* Max fd we will be selecting on. */
maxfd = MAX(sv[0], signal_pipe[0]);
saved_pgrp = -1;
}
if (signo == SIGTSTP) {
- zero_bytes(&sa, sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
- sigaction(SIGTSTP, &sa, &osa);
+ sudo_sigaction(SIGTSTP, &sa, &osa, false);
}
if (kill(getpid(), signo) != 0)
warning("kill(%d, SIG%s)", (int)getpid(), signame);
if (signo == SIGTSTP)
- sigaction(SIGTSTP, &osa, NULL);
+ sudo_sigaction(SIGTSTP, &osa, NULL, false);
if (fd != -1) {
/*
* Restore command's process group if different.
}
/*
- * Read pending signals on signale_pipe written by sudo_handler().
+ * Drain pending signals from signale_pipe written by sudo_handler().
* Handles the case where the signal was sent to us before
* we have executed the command.
* Returns 1 if we should terminate, else 0.
/* Only stop if we haven't already been terminated. */
if (signo == SIGTSTP)
{
- zero_bytes(&sa, sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
- sigaction(SIGTSTP, &sa, NULL);
+ sudo_sigaction(SIGTSTP, &sa, NULL, false);
if (kill(getpid(), SIGTSTP) != 0)
warning("kill(%d, SIGTSTP)", (int)getpid());
/* No need to reinstall SIGTSTP handler. */
/* Suspend self and continue command when we resume. */
if (signo != SIGSTOP) {
- zero_bytes(&sa, sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
+ sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
- sigaction(signo, &sa, &osa);
+ sudo_sigaction(signo, &sa, &osa, false);
}
sudo_debug_printf(SUDO_DEBUG_INFO, "kill parent SIG%s", signame);
if (killpg(ppgrp, signo) != 0)
}
if (signo != SIGSTOP)
- sigaction(signo, &osa, NULL);
+ sudo_sigaction(signo, &osa, NULL, false);
rval = ttymode == TERM_RAW ? SIGCONT_FG : SIGCONT_BG;
break;
}
ppgrp = getpgrp(); /* parent's pgrp, so child can signal us */
- zero_bytes(&sa, sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
if (io_fds[SFD_USERTTY] != -1) {
sa.sa_flags = SA_RESTART;
sa.sa_handler = sigwinch;
- sigaction(SIGWINCH, &sa, NULL);
+ sudo_sigaction(SIGWINCH, &sa, NULL, false);
}
/* So we can block tty-generated signals */
/* We don't want to receive SIGTTIN/SIGTTOU, getting EIO is preferable. */
sa.sa_handler = SIG_IGN;
- sigaction(SIGTTIN, &sa, NULL);
- sigaction(SIGTTOU, &sa, NULL);
+ sudo_sigaction(SIGTTIN, &sa, NULL, true);
+ sudo_sigaction(SIGTTOU, &sa, NULL, true);
/* Job control signals to relay from parent to child. */
sigfillset(&sa.sa_mask);
#else
sa.sa_handler = handler;
#endif
- sigaction(SIGTSTP, &sa, NULL);
+ sudo_sigaction(SIGTSTP, &sa, NULL, true);
if (foreground) {
/* Copy terminal attrs from user tty -> pty slave. */
error(1, _("unable to create pipe"));
/* Reset SIGWINCH and SIGALRM. */
- zero_bytes(&sa, sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
- sigaction(SIGWINCH, &sa, NULL);
- sigaction(SIGALRM, &sa, NULL);
+ sudo_sigaction(SIGWINCH, &sa, NULL, false);
+ sudo_sigaction(SIGALRM, &sa, NULL, false); /* XXX - saved value */
/* Ignore any SIGTTIN or SIGTTOU we get. */
sa.sa_handler = SIG_IGN;
- sigaction(SIGTTIN, &sa, NULL);
- sigaction(SIGTTOU, &sa, NULL);
+ sudo_sigaction(SIGTTIN, &sa, NULL, true);
+ sudo_sigaction(SIGTTOU, &sa, NULL, true);
/* Block all signals in mon_handler(). */
sigfillset(&sa.sa_mask);
#else
sa.sa_handler = mon_handler;
#endif
- sigaction(SIGCHLD, &sa, NULL);
+ sudo_sigaction(SIGCHLD, &sa, NULL, false);
/* Catch common signals so we can cleanup properly. */
sa.sa_flags = SA_RESTART;
#else
sa.sa_handler = mon_handler;
#endif
- sigaction(SIGHUP, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGQUIT, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
- sigaction(SIGTSTP, &sa, NULL);
- sigaction(SIGUSR1, &sa, NULL);
- sigaction(SIGUSR2, &sa, NULL);
+ sudo_sigaction(SIGHUP, &sa, NULL, true);
+ sudo_sigaction(SIGINT, &sa, NULL, true);
+ sudo_sigaction(SIGQUIT, &sa, NULL, true);
+ sudo_sigaction(SIGTERM, &sa, NULL, true);
+ sudo_sigaction(SIGTSTP, &sa, NULL, true);
+ sudo_sigaction(SIGUSR1, &sa, NULL, true);
+ sudo_sigaction(SIGUSR2, &sa, NULL, true);
/*
* Start a new session with the parent as the session leader
--- /dev/null
+/*
+ * Copyright (c) 2009-2012 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/types.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
+# 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 <signal.h>
+
+#include "sudo.h"
+
+int signal_pipe[2];
+
+static struct signal_state {
+ int signo;
+ sigaction_t sa;
+} saved_signals[] = {
+ { SIGALRM }, /* SAVED_SIGALRM */
+ { SIGCHLD }, /* SAVED_SIGCHLD */
+ { SIGCONT }, /* SAVED_SIGCONT */
+ { SIGHUP }, /* SAVED_SIGHUP */
+ { SIGINT }, /* SAVED_SIGINT */
+ { SIGPIPE }, /* SAVED_SIGPIPE */
+ { SIGQUIT }, /* SAVED_SIGQUIT */
+ { SIGTERM }, /* SAVED_SIGTERM */
+ { SIGTSTP }, /* SAVED_SIGTSTP */
+ { SIGTTIN }, /* SAVED_SIGTTIN */
+ { SIGTTOU }, /* SAVED_SIGTTOU */
+ { SIGUSR1 }, /* SAVED_SIGUSR1 */
+ { SIGUSR2 }, /* SAVED_SIGUSR2 */
+ { -1 }
+};
+
+/*
+ * Save signal handler state so it can be restored before exec.
+ */
+void
+save_signals(void)
+{
+ struct signal_state *ss;
+ debug_decl(save_signals, SUDO_DEBUG_MAIN)
+
+ for (ss = saved_signals; ss->signo != -1; ss++)
+ sigaction(ss->signo, NULL, &ss->sa);
+
+ debug_return;
+}
+
+/*
+ * Restore signal handlers to initial state for exec.
+ */
+void
+restore_signals(void)
+{
+ struct signal_state *ss;
+ debug_decl(restore_signals, SUDO_DEBUG_MAIN)
+
+ for (ss = saved_signals; ss->signo != -1; ss++)
+ sigaction(ss->signo, &ss->sa, NULL);
+
+ debug_return;
+}
+
+static void
+sudo_handler(int signo)
+{
+ /*
+ * The pipe is non-blocking, if we overflow the kernel's pipe
+ * buffer we drop the signal. This is not a problem in practice.
+ */
+ ignore_result(write(signal_pipe[1], &signo, sizeof(signo)));
+}
+
+/*
+ * Trap tty-generated (and other) signals so we can't be killed before
+ * calling the policy close function. The signal pipe will be drained
+ * in sudo_execute() before running the command and new handlers will
+ * be installed in the parent.
+ */
+void
+init_signals(void)
+{
+ struct sigaction sa;
+ struct signal_state *ss;
+ debug_decl(init_signals, SUDO_DEBUG_MAIN)
+
+ /*
+ * We use a pipe to atomically handle signal notification within
+ * the select() loop without races (we may not have pselect()).
+ */
+ if (pipe_nonblock(signal_pipe) != 0)
+ error(1, _("unable to create pipe"));
+
+ memset(&sa, 0, sizeof(sa));
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sudo_handler;
+
+ for (ss = saved_signals; ss->signo > 0; ss++) {
+ switch (ss->signo) {
+ case SIGCHLD:
+ case SIGCONT:
+ case SIGPIPE:
+ case SIGTTIN:
+ case SIGTTOU:
+ /* Don't install these until exec time. */
+ break;
+ default:
+ if (ss->sa.sa_handler != SIG_IGN)
+ sigaction(ss->signo, &sa, NULL);
+ break;
+ }
+ }
+ debug_return;
+}
+
+/*
+ * Like sigaction() but includes an udpate_only flag.
+ * In update-only mode, don't override SIG_IGN.
+ */
+int
+sudo_sigaction(int signo, struct sigaction *sa, struct sigaction *osa, bool update_only)
+{
+ /* Don't override SIG_IGN if the update_only flag is set. */
+ if (update_only) {
+ struct signal_state *ss;
+ for (ss = saved_signals; ss->signo > 0; ss++) {
+ if (ss->signo == signo) {
+ if (ss->sa.sa_handler == SIG_IGN)
+ return 0;
+ break;
+ }
+ }
+ }
+ return sigaction(signo, sa, osa);
+}
struct plugin_container_list io_plugins;
struct user_details user_details;
const char *list_user, *runas_user, *runas_group; /* extern for parse_args.c */
-int signal_pipe[2];
static int sudo_mode;
/*
static char **get_user_info(struct user_details *);
static void command_info_to_details(char * const info[],
struct command_details *details);
-static void setup_signals(void);
/* Policy plugin convenience functions. */
static int policy_open(struct plugin_container *plugin, char * const settings[],
errorx(1, _("unable to initialize policy plugin"));
}
- setup_signals();
+ init_signals();
switch (sudo_mode & MODE_MASK) {
case MODE_VERSION:
debug_return;
}
-
-static void
-sudo_handler(int signo)
-{
- /*
- * The pipe is non-blocking, if we overflow the kernel's pipe
- * buffer we drop the signal. This is not a problem in practice.
- */
- ignore_result(write(signal_pipe[1], &signo, sizeof(signo)));
-}
-
-/*
- * Trap tty-generated signals so we can't be killed before calling
- * the policy close function. The signal pipe will be checked
- * in sudo_execute().
- */
-static void
-setup_signals(void)
-{
- struct sigaction sa;
- debug_decl(setup_signals, SUDO_DEBUG_MAIN)
-
- /*
- * We use a pipe to atomically handle signal notification within
- * the select() loop without races (we may not have pselect()).
- */
- if (pipe_nonblock(signal_pipe) != 0)
- error(1, _("unable to create pipe"));
-
- /* XXX - should not install handler if ignored by default. */
- memset(&sa, 0, sizeof(sa));
- sigfillset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = sudo_handler;
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGQUIT, &sa, NULL);
- sigaction(SIGTSTP, &sa, NULL);
-
- debug_return;
-}
/* exec.c */
int pipe_nonblock(int fds[2]);
int sudo_execute(struct command_details *details, struct command_status *cstat);
-void restore_signals(void);
-void save_signals(void);
/* term.c */
int term_cbreak(int);
int os_init_common(int argc, char *argv[], char *envp[]);
extern const char *list_user, *runas_user, *runas_group;
extern struct user_details user_details;
-extern int signal_pipe[2];
/* sudo_edit.c */
int sudo_edit(struct command_details *details);
/* ttyname.c */
char *get_process_ttyname(void);
+/* signal.c */
+struct sigaction;
+extern int signal_pipe[2];
+int sudo_sigaction(int signo, struct sigaction *sa, struct sigaction *osa, bool update_only);
+void init_signals(void);
+void restore_signals(void);
+void save_signals(void);
+
#endif /* _SUDO_SUDO_H */