Move code to suspend sudo when no pty is in use to separate file.

Use this in test_ptrace.c to be able to suspend just like sudo does.
This commit is contained in:
Todd C. Miller
2022-05-18 07:29:55 -06:00
parent a52e3776f0
commit 0ea431e392
7 changed files with 190 additions and 118 deletions

View File

@@ -1222,6 +1222,7 @@ src/sudo_intercept_common.c
src/sudo_noexec.c
src/sudo_plugin_int.h
src/sudo_usage.h.in
src/suspend_nopty.c
src/tcsetpgrp_nobg.c
src/tgetpass.c
src/ttyname.c

View File

@@ -144,7 +144,7 @@ OBJS = conversation.o copy_file.o edit_open.o env_hooks.o exec.o exec_common.o \
exec_intercept.o exec_monitor.o exec_nopty.o exec_preload.o \
exec_ptrace.o exec_pty.o get_pty.o hooks.o limits.o load_plugins.o \
net_ifs.o parse_args.o preserve_fds.o signal.o sudo.o sudo_edit.o \
tcsetpgrp_nobg.o tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
suspend_nopty.o tcsetpgrp_nobg.o tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
IOBJS = $(OBJS:.o=.i) sesh.i
@@ -161,7 +161,7 @@ CHECK_NOEXEC_OBJS = check_noexec.o exec_common.o exec_preload.o
CHECK_TTYNAME_OBJS = check_ttyname.o ttyname.o
TEST_PTRACE_OBJS = test_ptrace.o
TEST_PTRACE_OBJS = suspend_nopty.o tcsetpgrp_nobg.o test_ptrace.o
LIBOBJDIR = $(top_builddir)/@ac_config_libobj_dir@/
@@ -614,23 +614,25 @@ exec_preload.o: $(srcdir)/exec_preload.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_util.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/exec_preload.c
exec_ptrace.o: $(srcdir)/exec_ptrace.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/exec_intercept.h \
$(srcdir)/exec_ptrace.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
exec_ptrace.o: $(srcdir)/exec_ptrace.c $(incdir)/compat/endian.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/exec_intercept.h $(srcdir)/exec_ptrace.h \
$(srcdir)/sudo.h $(srcdir)/sudo_exec.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/exec_ptrace.c
exec_ptrace.i: $(srcdir)/exec_ptrace.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/exec_intercept.h \
$(srcdir)/exec_ptrace.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
exec_ptrace.i: $(srcdir)/exec_ptrace.c $(incdir)/compat/endian.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/exec_intercept.h $(srcdir)/exec_ptrace.h \
$(srcdir)/sudo.h $(srcdir)/sudo_exec.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(CC) -E -o $@ $(CPPFLAGS) $<
exec_ptrace.plog: exec_ptrace.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/exec_ptrace.c --i-file $< --output-file $@
@@ -953,6 +955,24 @@ sudo_intercept_common.i: $(srcdir)/sudo_intercept_common.c \
$(CC) -E -o $@ $(CPPFLAGS) $<
sudo_intercept_common.plog: sudo_intercept_common.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/sudo_intercept_common.c --i-file $< --output-file $@
suspend_nopty.o: $(srcdir)/suspend_nopty.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/suspend_nopty.c
suspend_nopty.i: $(srcdir)/suspend_nopty.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(CC) -E -o $@ $(CPPFLAGS) $<
suspend_nopty.plog: suspend_nopty.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/suspend_nopty.c --i-file $< --output-file $@
tcsetpgrp_nobg.o: $(srcdir)/tcsetpgrp_nobg.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
@@ -972,24 +992,26 @@ tcsetpgrp_nobg.i: $(srcdir)/tcsetpgrp_nobg.c $(incdir)/compat/stdbool.h \
tcsetpgrp_nobg.plog: tcsetpgrp_nobg.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/tcsetpgrp_nobg.c --i-file $< --output-file $@
test_ptrace.o: $(srcdir)/regress/intercept/test_ptrace.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/exec_intercept.h $(srcdir)/exec_ptrace.c \
$(srcdir)/exec_ptrace.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(incdir)/compat/endian.h $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/exec_intercept.h \
$(srcdir)/exec_ptrace.c $(srcdir)/exec_ptrace.h \
$(srcdir)/sudo.h $(srcdir)/sudo_exec.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/regress/intercept/test_ptrace.c
test_ptrace.i: $(srcdir)/regress/intercept/test_ptrace.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/exec_intercept.h $(srcdir)/exec_ptrace.c \
$(srcdir)/exec_ptrace.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(incdir)/compat/endian.h $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/exec_intercept.h \
$(srcdir)/exec_ptrace.c $(srcdir)/exec_ptrace.h \
$(srcdir)/sudo.h $(srcdir)/sudo_exec.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(CC) -E -o $@ $(CPPFLAGS) $<
test_ptrace.plog: test_ptrace.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/intercept/test_ptrace.c --i-file $< --output-file $@

View File

@@ -558,9 +558,7 @@ handle_sigchld_nopty(struct exec_closure_nopty *ec)
}
if (WIFSTOPPED(status)) {
struct sigaction sa, osa;
pid_t saved_pgrp = -1;
int fd, signo = WSTOPSIG(status);
const int signo = WSTOPSIG(status);
if (sig2str(signo, signame) == -1)
(void)snprintf(signame, sizeof(signame), "%d", signo);
@@ -573,79 +571,9 @@ handle_sigchld_nopty(struct exec_closure_nopty *ec)
continue;
}
/* Special handling for job control in the main command only. */
if (pid != ec->cmnd_pid)
continue;
/*
* Save the controlling terminal's process group so we can restore
* it after we resume, if needed. Most well-behaved shells change
* the pgrp back to its original value before suspending so we must
* not try to restore in that case, lest we race with the command
* upon resume, potentially stopping sudo with SIGTTOU while the
* command continues to run.
*/
fd = open(_PATH_TTY, O_RDWR);
if (fd != -1) {
saved_pgrp = tcgetpgrp(fd);
if (saved_pgrp == -1) {
close(fd);
fd = -1;
}
}
if (saved_pgrp != -1) {
/*
* Command was stopped trying to access the controlling
* terminal. If the command has a different pgrp and we
* own the controlling terminal, give it to the command's
* pgrp and let it continue.
*/
if (signo == SIGTTOU || signo == SIGTTIN) {
if (saved_pgrp == ec->ppgrp) {
pid_t cmnd_pgrp = getpgid(ec->cmnd_pid);
if (cmnd_pgrp != ec->ppgrp) {
if (tcsetpgrp_nobg(fd, cmnd_pgrp) == 0) {
if (killpg(cmnd_pgrp, SIGCONT) != 0) {
sudo_warn("kill(%d, SIGCONT)",
(int)cmnd_pgrp);
}
close(fd);
continue;
}
}
}
}
}
if (signo == SIGTSTP) {
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
if (sudo_sigaction(SIGTSTP, &sa, &osa) != 0) {
sudo_warn(U_("unable to set handler for signal %d"),
SIGTSTP);
}
}
if (kill(getpid(), signo) != 0)
sudo_warn("kill(%d, SIG%s)", (int)getpid(), signame);
if (signo == SIGTSTP) {
if (sudo_sigaction(SIGTSTP, &osa, NULL) != 0) {
sudo_warn(U_("unable to restore handler for signal %d"),
SIGTSTP);
}
}
if (saved_pgrp != -1) {
/*
* On resume, restore foreground process group, if different.
* Otherwise, we cannot resume some shells (pdksh).
*
* It is possible that we are no longer the foreground process,
* use tcsetpgrp_nobg() to prevent sudo from receiving SIGTTOU.
*/
if (saved_pgrp != ec->ppgrp)
tcsetpgrp_nobg(fd, saved_pgrp);
close(fd);
}
/* If the main command is suspended, suspend sudo too. */
if (pid == ec->cmnd_pid)
suspend_sudo_nopty(signo, ec->ppgrp, ec->cmnd_pid);
} else {
if (WIFSIGNALED(status)) {
if (sig2str(WTERMSIG(status), signame) == -1) {

View File

@@ -528,12 +528,12 @@ check_foreground(struct exec_closure_pty *ec)
* foreground or SIGCONT_BG if it is a background process.
*/
static int
suspend_sudo(struct exec_closure_pty *ec, int signo)
suspend_sudo_pty(struct exec_closure_pty *ec, int signo)
{
char signame[SIG2STR_MAX];
struct sigaction sa, osa;
int ret = 0;
debug_decl(suspend_sudo, SUDO_DEBUG_EXEC);
debug_decl(suspend_sudo_pty, SUDO_DEBUG_EXEC);
switch (signo) {
case SIGTTOU:
@@ -1015,7 +1015,7 @@ backchannel_cb(int fd, int what, void *v)
/* Suspend parent and tell monitor how to resume on return. */
sudo_debug_printf(SUDO_DEBUG_INFO,
"command stopped, suspending parent");
signo = suspend_sudo(ec, WSTOPSIG(cstat.val));
signo = suspend_sudo_pty(ec, WSTOPSIG(cstat.val));
schedule_signal(ec, signo);
/* Re-enable I/O events */
add_io_events(ec->evbase);
@@ -1111,7 +1111,7 @@ handle_sigchld_pty(struct exec_closure_pty *ec)
*/
sudo_debug_printf(SUDO_DEBUG_INFO,
"monitor stopped, suspending sudo");
n = suspend_sudo(ec, WSTOPSIG(status));
n = suspend_sudo_pty(ec, WSTOPSIG(status));
kill(pid, SIGCONT);
schedule_signal(ec, n);
/* Re-enable I/O events */

View File

@@ -97,6 +97,12 @@ init_debug_files(struct sudo_conf_debug_file_list *file_list,
debug_return;
}
int
sudo_sigaction(int signo, struct sigaction *sa, struct sigaction *osa)
{
return sigaction(signo, sa, osa);
}
int
main(int argc, char *argv[])
{
@@ -107,7 +113,7 @@ main(int argc, char *argv[])
const char *errstr;
sigset_t blocked, empty;
struct sigaction sa;
pid_t child, pid;
pid_t child, pid, ppgrp;
int ch, status;
debug_decl_vars(main, SUDO_DEBUG_MAIN);
@@ -159,6 +165,7 @@ main(int argc, char *argv[])
sigaction(SIGUSR1, &sa, NULL);
/* Fork a shell. */
ppgrp = getpgrp();
child = fork();
switch (child) {
case -1:
@@ -212,9 +219,9 @@ main(int argc, char *argv[])
} else if (WIFSTOPPED(status)) {
if (exec_ptrace_stopped(pid, status, &closure)) {
if (pid == child) {
/* TODO - terminal pgrp switching like exec_nopty.c. */
kill(getpid(), WSTOPSIG(status));
kill(child, SIGCONT);
suspend_sudo_nopty(WSTOPSIG(status), ppgrp, child);
if (kill(child, SIGCONT) != 0)
sudo_warn("kill(%d, SIGCONT)", (int)child);
}
}
} else {

View File

@@ -151,4 +151,7 @@ bool exec_ptrace_stopped(pid_t pid, int status, void *intercept);
bool set_exec_filter(void);
int exec_ptrace_seize(pid_t child);
/* suspend_nopty.c */
void suspend_sudo_nopty(int signo, pid_t ppgrp, pid_t cmnd_pid);
#endif /* SUDO_EXEC_H */

111
src/suspend_nopty.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2009-2022 Todd C. Miller <Todd.Miller@sudo.ws>
*
* 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.
*/
/*
* This is an open source non-commercial project. Dear PVS-Studio, please check it.
* PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include "sudo.h"
#include "sudo_exec.h"
void
suspend_sudo_nopty(int signo, pid_t ppgrp, pid_t cmnd_pid)
{
struct sigaction sa, osa;
pid_t saved_pgrp = -1;
int fd;
debug_decl(suspend_sudo_nopty, SUDO_DEBUG_EXEC);
/*
* Save the controlling terminal's process group so we can restore
* it after we resume, if needed. Most well-behaved shells change
* the pgrp back to its original value before suspending so we must
* not try to restore in that case, lest we race with the command
* upon resume, potentially stopping sudo with SIGTTOU while the
* command continues to run.
*/
fd = open(_PATH_TTY, O_RDWR);
if (fd != -1) {
saved_pgrp = tcgetpgrp(fd);
if (saved_pgrp == -1) {
close(fd);
fd = -1;
}
}
if (saved_pgrp != -1) {
/*
* Command was stopped trying to access the controlling
* terminal. If the command has a different pgrp and we
* own the controlling terminal, give it to the command's
* pgrp and let it continue.
*/
if (signo == SIGTTOU || signo == SIGTTIN) {
if (saved_pgrp == ppgrp) {
pid_t cmnd_pgrp = getpgid(cmnd_pid);
if (cmnd_pgrp != ppgrp) {
if (tcsetpgrp_nobg(fd, cmnd_pgrp) == 0) {
if (killpg(cmnd_pgrp, SIGCONT) != 0)
sudo_warn("kill(%d, SIGCONT)", (int)cmnd_pgrp);
close(fd);
debug_return;
}
}
}
}
}
if (signo == SIGTSTP) {
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
if (sudo_sigaction(SIGTSTP, &sa, &osa) != 0)
sudo_warn(U_("unable to set handler for signal %d"), SIGTSTP);
}
if (kill(getpid(), signo) != 0)
sudo_warn("kill(%d, %d)", (int)getpid(), signo);
if (signo == SIGTSTP) {
if (sudo_sigaction(SIGTSTP, &osa, NULL) != 0)
sudo_warn(U_("unable to restore handler for signal %d"), SIGTSTP);
}
if (saved_pgrp != -1) {
/*
* On resume, restore foreground process group, if different.
* Otherwise, we cannot resume some shells (pdksh).
*
* It is possible that we are no longer the foreground process,
* use tcsetpgrp_nobg() to prevent sudo from receiving SIGTTOU.
*/
if (saved_pgrp != ppgrp)
tcsetpgrp_nobg(fd, saved_pgrp);
close(fd);
}
debug_return;
}