In ptrace(2) intercept mode, add execveat to the seccomp(2) filter.
This allows us to avoid logging the initial command twice regardless of whether the kernel supports execveat(2) or not.
This commit is contained in:
@@ -76,13 +76,9 @@ intercept_setup(int fd, struct sudo_event_base *evbase,
|
||||
* We can perform a policy check immediately using ptrace(2)
|
||||
* but should ignore the execve(2) of the initial command
|
||||
* (and sesh for SELinux RBAC).
|
||||
*
|
||||
* If using fexecve(2) and the system doesn't support execveat(2),
|
||||
* we may end up checking the initial command anyway.
|
||||
*/
|
||||
closure->state = RECV_POLICY_CHECK;
|
||||
if (!ISSET(details->flags, CD_FEXECVE))
|
||||
closure->initial_command++;
|
||||
closure->initial_command = 1;
|
||||
if (ISSET(details->flags, CD_RBAC_ENABLED))
|
||||
closure->initial_command++;
|
||||
} else {
|
||||
|
@@ -49,6 +49,11 @@
|
||||
# include <linux/seccomp.h>
|
||||
# include <linux/filter.h>
|
||||
|
||||
/* Older systems may not support execveat(2). */
|
||||
#ifndef __NR_execveat
|
||||
# define __NR_execveat -1
|
||||
#endif
|
||||
|
||||
/* Align address to a word boundary. */
|
||||
#define WORDALIGN(_a) (((_a) + (sizeof(long) - 1L)) & ~(sizeof(long) - 1L))
|
||||
|
||||
@@ -60,6 +65,9 @@
|
||||
* We define user_regs_struct as the struct to use for the
|
||||
* PTRACE_GETREGSET/PTRACE_SETREGSET command and define accessor
|
||||
* macros to get/set the struct members.
|
||||
*
|
||||
* The value of SECCOMP_AUDIT_ARCH is used when matching the architecture
|
||||
* in the seccomp(2) filter.
|
||||
*/
|
||||
#if defined(__amd64__)
|
||||
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64
|
||||
@@ -351,8 +359,7 @@ get_execve_info(pid_t pid, struct user_pt_regs *regs, char **pathname_out,
|
||||
int *argc_out, char ***argv_out, int *envc_out, char ***envp_out)
|
||||
{
|
||||
char *argbuf, *strtab, *pathname, **argv, **envp;
|
||||
long path_addr, argv_addr, envp_addr, syscallno;
|
||||
struct iovec iov;
|
||||
long path_addr, argv_addr, envp_addr;
|
||||
int argc, envc;
|
||||
size_t bufsize, len;
|
||||
debug_decl(get_execve_info, SUDO_DEBUG_EXEC);
|
||||
@@ -362,21 +369,6 @@ get_execve_info(pid_t pid, struct user_pt_regs *regs, char **pathname_out,
|
||||
if (argbuf == NULL)
|
||||
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
|
||||
/* XXX - for amd64 and i386 use PTRACE_GETREGS/PTRACE_SETREGS instead. */
|
||||
iov.iov_base = regs;
|
||||
iov.iov_len = sizeof(*regs);
|
||||
if (ptrace(PTRACE_GETREGSET, pid, (long)NT_PRSTATUS, &iov) == -1) {
|
||||
sudo_warn(U_("unable to get registers for process %d"), (int)pid);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* System call number is stored in the lower 32-bits on 64-bit platforms. */
|
||||
syscallno = reg_syscall(regs) & 0xffffffff;
|
||||
if (syscallno != __NR_execve) {
|
||||
sudo_warnx("%s: unexpected system call %ld", __func__, syscallno);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* execve(2) takes three arguments: pathname, argv, envp. */
|
||||
path_addr = reg_arg1(regs);
|
||||
argv_addr = reg_arg2(regs);
|
||||
@@ -526,8 +518,8 @@ have_seccomp_action(const char *action)
|
||||
}
|
||||
|
||||
/*
|
||||
* Intercept execve(2) using seccomp(2) and ptrace(2).
|
||||
* If no tracer is present, execve(2) will fail with ENOSYS.
|
||||
* Intercept execve(2) and execveat(2) using seccomp(2) and ptrace(2).
|
||||
* If no tracer is present, execve(2) and execveat(2) will fail with ENOSYS.
|
||||
* Must be called with CAP_SYS_ADMIN, before privs are dropped.
|
||||
*/
|
||||
bool
|
||||
@@ -537,12 +529,13 @@ set_exec_filter(void)
|
||||
/* Load architecture value (AUDIT_ARCH_*) into the accumulator. */
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),
|
||||
/* Jump to the end unless the architecture matches. */
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_AUDIT_ARCH, 0, 3),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_AUDIT_ARCH, 0, 4),
|
||||
/* Load syscall number into the accumulator. */
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
|
||||
/* Jump to trace for execve(2), else allow. */
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 0, 1),
|
||||
/* Trace execve(2) syscall */
|
||||
/* Jump to trace for execve(2)/execveat(2), else allow. */
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 1, 0),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execveat, 0, 1),
|
||||
/* Trace execve(2)/execveat(2) syscalls */
|
||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE),
|
||||
/* Allow non-matching syscalls */
|
||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
|
||||
@@ -617,10 +610,11 @@ static bool
|
||||
ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure)
|
||||
{
|
||||
char *pathname, **argv, **envp, *buf;
|
||||
int argc, envc, syscallno;
|
||||
struct user_pt_regs regs;
|
||||
char cwd[PATH_MAX];
|
||||
struct iovec iov;
|
||||
bool ret = false;
|
||||
int argc, envc;
|
||||
debug_decl(ptrace_intercept_execve, SUDO_DEBUG_UTIL);
|
||||
|
||||
/* Do not check the policy if we are executing the initial command. */
|
||||
@@ -629,6 +623,30 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure)
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
/* Get the registers. */
|
||||
/* XXX - for amd64 and i386 use PTRACE_GETREGS/PTRACE_SETREGS instead. */
|
||||
iov.iov_base = ®s;
|
||||
iov.iov_len = sizeof(regs);
|
||||
if (ptrace(PTRACE_GETREGSET, pid, (long)NT_PRSTATUS, &iov) == -1) {
|
||||
sudo_warn(U_("unable to get registers for process %d"), (int)pid);
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
/* System call number is stored in the lower 32-bits on 64-bit platforms. */
|
||||
syscallno = reg_syscall(®s) & 0xffffffff;
|
||||
switch (syscallno) {
|
||||
case __NR_execve:
|
||||
/* Handled below. */
|
||||
break;
|
||||
case __NR_execveat:
|
||||
/* We don't currently check execveat(2). */
|
||||
debug_return_bool(true);
|
||||
break;
|
||||
default:
|
||||
sudo_warnx("%s: unexpected system call %d", __func__, syscallno);
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
/* Get the current working directory and execve info. */
|
||||
if (!getcwd_by_pid(pid, cwd, sizeof(cwd)))
|
||||
(void)strlcpy(cwd, "unknown", sizeof(cwd));
|
||||
|
Reference in New Issue
Block a user