diff --git a/src/exec_nopty.c b/src/exec_nopty.c index 01c41bb9c..059d9994f 100644 --- a/src/exec_nopty.c +++ b/src/exec_nopty.c @@ -476,18 +476,21 @@ exec_nopty(struct command_details *details, struct command_status *cstat) fill_exec_closure_nopty(&ec, cstat, details, errpipe[0]); if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) { - bool success = true; + int rc = 1; /* Create event and closure for intercept mode. */ ec.intercept = intercept_setup(intercept_sv[0], ec.evbase, details); if (ec.intercept == NULL) { - success = false; + rc = -1; } else if (ISSET(details->flags, CD_USE_PTRACE)) { - /* Seize control of the command using ptrace(2). */ - if (!exec_ptrace_seize(ec.cmnd_pid)) - success = false; + /* Try to seize control of the command using ptrace(2). */ + rc = exec_ptrace_seize(ec.cmnd_pid); + 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); } diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c index cb0e39023..45ea028dd 100644 --- a/src/exec_ptrace.c +++ b/src/exec_ptrace.c @@ -611,27 +611,43 @@ set_exec_filter(void) /* * 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) { const long ptrace_opts = PTRACE_O_TRACESECCOMP|PTRACE_O_TRACECLONE| PTRACE_O_TRACEFORK|PTRACE_O_TRACEVFORK; + int ret = -1; int status; debug_decl(exec_ptrace_seize, SUDO_DEBUG_UTIL); /* Seize control of the child process. */ if (ptrace(PTRACE_SEIZE, child, NULL, ptrace_opts) == -1) { - sudo_warn("ptrace(PTRACE_SEIZE, %d, NULL, 0x%lx)", (int)child, - ptrace_opts); - debug_return_bool(false); + /* + * If the process is already being traced, we will get EPERM. + * 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. */ if (kill(child, SIGUSR1) == -1) { 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. */ for (;;) { @@ -640,18 +656,21 @@ exec_ptrace_seize(pid_t child) if (errno == EINTR) continue; sudo_warn(U_("%s: %s"), __func__, "waitpid"); - debug_return_bool(false); + goto done; } if (!WIFSTOPPED(status)) { sudo_warnx(U_("process %d exited unexpectedly"), (int)child); - debug_return_bool(false); + goto done; } if (ptrace(PTRACE_CONT, child, NULL, (long)SIGUSR1) == -1) { 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 */ -bool +int exec_ptrace_seize(pid_t child) { return true; diff --git a/src/exec_pty.c b/src/exec_pty.c index 3e9250499..3aece40d5 100644 --- a/src/exec_pty.c +++ b/src/exec_pty.c @@ -994,8 +994,12 @@ backchannel_cb(int fd, int what, void *v) sudo_debug_printf(SUDO_DEBUG_INFO, "executed %s, pid %d", ec->details->command, (int)ec->cmnd_pid); if (ISSET(ec->details->flags, CD_USE_PTRACE)) { - /* Seize control of the command using ptrace(2). */ - if (!exec_ptrace_seize(ec->cmnd_pid)) { + /* Try to seize control of the command using ptrace(2). */ + 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) { ec->cstat->type = CMD_ERRNO; ec->cstat->val = errno; diff --git a/src/sudo_exec.h b/src/sudo_exec.h index fcc6e6f2a..4efbdb38b 100644 --- a/src/sudo_exec.h +++ b/src/sudo_exec.h @@ -146,8 +146,8 @@ char **sudo_preload_dso(char *envp[], const char *dso_file, int intercept_fd); /* exec_ptrace.c */ 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 set_exec_filter(void); +int exec_ptrace_seize(pid_t child); #endif /* SUDO_EXEC_H */