If the process is already being traced, just resume it and clear flags.
This makes it possible to run sudo in ptrace intercept mode from within a shell (or other process) that is already being traced by sudo.
This commit is contained in:
@@ -476,18 +476,21 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
|
|||||||
fill_exec_closure_nopty(&ec, cstat, details, errpipe[0]);
|
fill_exec_closure_nopty(&ec, cstat, details, errpipe[0]);
|
||||||
|
|
||||||
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
||||||
bool success = true;
|
int rc = 1;
|
||||||
|
|
||||||
/* Create event and closure for intercept mode. */
|
/* Create event and closure for intercept mode. */
|
||||||
ec.intercept = intercept_setup(intercept_sv[0], ec.evbase, details);
|
ec.intercept = intercept_setup(intercept_sv[0], ec.evbase, details);
|
||||||
if (ec.intercept == NULL) {
|
if (ec.intercept == NULL) {
|
||||||
success = false;
|
rc = -1;
|
||||||
} else if (ISSET(details->flags, CD_USE_PTRACE)) {
|
} else if (ISSET(details->flags, CD_USE_PTRACE)) {
|
||||||
/* Seize control of the command using ptrace(2). */
|
/* Try to seize control of the command using ptrace(2). */
|
||||||
if (!exec_ptrace_seize(ec.cmnd_pid))
|
rc = exec_ptrace_seize(ec.cmnd_pid);
|
||||||
success = false;
|
if (rc == 0) {
|
||||||
|
/* There is another tracer present. */
|
||||||
|
CLR(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS|CD_USE_PTRACE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!success)
|
if (rc == -1)
|
||||||
terminate_command(ec.cmnd_pid, true);
|
terminate_command(ec.cmnd_pid, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -611,27 +611,43 @@ set_exec_filter(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Seize control of the specified child process which must be in
|
* Seize control of the specified child process which must be in
|
||||||
* ptrace wait. Returns true on success and false on failure.
|
* ptrace wait. Returns true on success, false if child is already
|
||||||
|
* being traced and -1 on error.
|
||||||
*/
|
*/
|
||||||
bool
|
int
|
||||||
exec_ptrace_seize(pid_t child)
|
exec_ptrace_seize(pid_t child)
|
||||||
{
|
{
|
||||||
const long ptrace_opts = PTRACE_O_TRACESECCOMP|PTRACE_O_TRACECLONE|
|
const long ptrace_opts = PTRACE_O_TRACESECCOMP|PTRACE_O_TRACECLONE|
|
||||||
PTRACE_O_TRACEFORK|PTRACE_O_TRACEVFORK;
|
PTRACE_O_TRACEFORK|PTRACE_O_TRACEVFORK;
|
||||||
|
int ret = -1;
|
||||||
int status;
|
int status;
|
||||||
debug_decl(exec_ptrace_seize, SUDO_DEBUG_UTIL);
|
debug_decl(exec_ptrace_seize, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
/* Seize control of the child process. */
|
/* Seize control of the child process. */
|
||||||
if (ptrace(PTRACE_SEIZE, child, NULL, ptrace_opts) == -1) {
|
if (ptrace(PTRACE_SEIZE, child, NULL, ptrace_opts) == -1) {
|
||||||
sudo_warn("ptrace(PTRACE_SEIZE, %d, NULL, 0x%lx)", (int)child,
|
/*
|
||||||
ptrace_opts);
|
* If the process is already being traced, we will get EPERM.
|
||||||
debug_return_bool(false);
|
* We don't treat that as a fatal error since we want it to be
|
||||||
|
* possible to run sudo inside a sudo shell with intercept enabled.
|
||||||
|
*/
|
||||||
|
if (errno != EPERM) {
|
||||||
|
sudo_warn("ptrace(PTRACE_SEIZE, %d, NULL, 0x%lx)", (int)child,
|
||||||
|
ptrace_opts);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_WARN,
|
||||||
|
"%s: unable to trace process %d, already being traced?",
|
||||||
|
__func__, (int)child);
|
||||||
|
ret = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The child is suspended waiting for SIGUSR1, wake it up. */
|
/* The child is suspended waiting for SIGUSR1, wake it up. */
|
||||||
if (kill(child, SIGUSR1) == -1) {
|
if (kill(child, SIGUSR1) == -1) {
|
||||||
sudo_warn("kill(%d, SIGUSR1)", child);
|
sudo_warn("kill(%d, SIGUSR1)", child);
|
||||||
debug_return_bool(false);
|
goto done;
|
||||||
}
|
}
|
||||||
|
if (!ret)
|
||||||
|
goto done;
|
||||||
|
|
||||||
/* Wait for the child to enter trace stop and continue it. */
|
/* Wait for the child to enter trace stop and continue it. */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -640,18 +656,21 @@ exec_ptrace_seize(pid_t child)
|
|||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
sudo_warn(U_("%s: %s"), __func__, "waitpid");
|
sudo_warn(U_("%s: %s"), __func__, "waitpid");
|
||||||
debug_return_bool(false);
|
goto done;
|
||||||
}
|
}
|
||||||
if (!WIFSTOPPED(status)) {
|
if (!WIFSTOPPED(status)) {
|
||||||
sudo_warnx(U_("process %d exited unexpectedly"), (int)child);
|
sudo_warnx(U_("process %d exited unexpectedly"), (int)child);
|
||||||
debug_return_bool(false);
|
goto done;
|
||||||
}
|
}
|
||||||
if (ptrace(PTRACE_CONT, child, NULL, (long)SIGUSR1) == -1) {
|
if (ptrace(PTRACE_CONT, child, NULL, (long)SIGUSR1) == -1) {
|
||||||
sudo_warn("ptrace(PTRACE_CONT, %d, NULL, SIGUSR1)", (int)child);
|
sudo_warn("ptrace(PTRACE_CONT, %d, NULL, SIGUSR1)", (int)child);
|
||||||
debug_return_bool(false);
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_return_bool(true);
|
ret = true;
|
||||||
|
|
||||||
|
done:
|
||||||
|
debug_return_int(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -921,7 +940,7 @@ exec_ptrace_handled(pid_t pid, int status, void *intercept)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* STUB */
|
/* STUB */
|
||||||
bool
|
int
|
||||||
exec_ptrace_seize(pid_t child)
|
exec_ptrace_seize(pid_t child)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@@ -994,8 +994,12 @@ backchannel_cb(int fd, int what, void *v)
|
|||||||
sudo_debug_printf(SUDO_DEBUG_INFO, "executed %s, pid %d",
|
sudo_debug_printf(SUDO_DEBUG_INFO, "executed %s, pid %d",
|
||||||
ec->details->command, (int)ec->cmnd_pid);
|
ec->details->command, (int)ec->cmnd_pid);
|
||||||
if (ISSET(ec->details->flags, CD_USE_PTRACE)) {
|
if (ISSET(ec->details->flags, CD_USE_PTRACE)) {
|
||||||
/* Seize control of the command using ptrace(2). */
|
/* Try to seize control of the command using ptrace(2). */
|
||||||
if (!exec_ptrace_seize(ec->cmnd_pid)) {
|
int rc = exec_ptrace_seize(ec->cmnd_pid);
|
||||||
|
if (rc == 0) {
|
||||||
|
/* There is another tracer present. */
|
||||||
|
CLR(ec->details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS|CD_USE_PTRACE);
|
||||||
|
} else if (rc == -1) {
|
||||||
if (ec->cstat->type == CMD_INVALID) {
|
if (ec->cstat->type == CMD_INVALID) {
|
||||||
ec->cstat->type = CMD_ERRNO;
|
ec->cstat->type = CMD_ERRNO;
|
||||||
ec->cstat->val = errno;
|
ec->cstat->val = errno;
|
||||||
|
@@ -146,8 +146,8 @@ char **sudo_preload_dso(char *envp[], const char *dso_file, int intercept_fd);
|
|||||||
|
|
||||||
/* exec_ptrace.c */
|
/* exec_ptrace.c */
|
||||||
bool exec_ptrace_handled(pid_t pid, int status, void *intercept);
|
bool exec_ptrace_handled(pid_t pid, int status, void *intercept);
|
||||||
bool exec_ptrace_seize(pid_t child);
|
|
||||||
bool have_seccomp_action(const char *action);
|
bool have_seccomp_action(const char *action);
|
||||||
bool set_exec_filter(void);
|
bool set_exec_filter(void);
|
||||||
|
int exec_ptrace_seize(pid_t child);
|
||||||
|
|
||||||
#endif /* SUDO_EXEC_H */
|
#endif /* SUDO_EXEC_H */
|
||||||
|
Reference in New Issue
Block a user