356 lines
12 KiB
C
356 lines
12 KiB
C
/*
|
|
* Copyright (c) 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.
|
|
*/
|
|
|
|
#ifndef SUDO_EXEC_PTRACE_H
|
|
#define SUDO_EXEC_PTRACE_H
|
|
|
|
#include <elf.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/ptrace.h>
|
|
#include <sys/user.h>
|
|
#include <asm/unistd.h>
|
|
#include <linux/audit.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/seccomp.h>
|
|
#include <linux/filter.h>
|
|
|
|
/* Older systems may not support execveat(2). */
|
|
#ifndef __NR_execveat
|
|
# define __NR_execveat -1
|
|
#endif
|
|
|
|
/* In case userland doesn't define __X32_SYSCALL_BIT. */
|
|
#if defined(__x86_64__) && !defined(__X32_SYSCALL_BIT)
|
|
# define __X32_SYSCALL_BIT 0x40000000
|
|
#endif
|
|
|
|
/* Align address to a (compat) word boundary. */
|
|
#define WORDALIGN(_a, _r) \
|
|
(((_a) + ((long)(_r).wordsize - 1L)) & ~((long)(_r).wordsize - 1L))
|
|
|
|
/*
|
|
* See syscall(2) for a list of registers used in system calls.
|
|
* For example code, see tools/testing/selftests/seccomp/seccomp_bpf.c
|
|
*
|
|
* The structs and registers vary among the different platforms.
|
|
* We define user_regs_struct as the struct to use for gettings
|
|
* and setting the general registers and define accessor
|
|
* macros to get/set the individual struct members.
|
|
*
|
|
* The value of SECCOMP_AUDIT_ARCH is used when matching the architecture
|
|
* in the seccomp(2) filter.
|
|
*/
|
|
#if defined(__x86_64__)
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64
|
|
# define X32_execve __X32_SYSCALL_BIT + 520
|
|
# define X32_execveat __X32_SYSCALL_BIT + 545
|
|
# define sudo_pt_regs struct user_regs_struct
|
|
# define reg_syscall(x) (x).orig_rax
|
|
# define reg_retval(x) (x).rax
|
|
# define reg_sp(x) (x).rsp
|
|
# define reg_arg1(x) (x).rdi
|
|
# define reg_arg2(x) (x).rsi
|
|
# define reg_arg3(x) (x).rdx
|
|
# define reg_arg4(x) (x).r10
|
|
#elif defined(__aarch64__)
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64
|
|
# define reg_syscall(x) (x).regs[8] /* w8 */
|
|
# define reg_retval(x) (x).regs[0] /* x0 */
|
|
# define reg_sp(x) (x).sp /* sp */
|
|
# define reg_arg1(x) (x).regs[0] /* x0 */
|
|
# define reg_arg2(x) (x).regs[1] /* x1 */
|
|
# define reg_arg3(x) (x).regs[2] /* x2 */
|
|
# define reg_arg4(x) (x).regs[3] /* x3 */
|
|
# define reg_set_syscall(_r, _nr) do { \
|
|
struct iovec _iov; \
|
|
long syscallno = (_nr); \
|
|
_iov.iov_base = &syscallno; \
|
|
_iov.iov_len = sizeof(syscallno); \
|
|
ptrace(PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL, &_iov); \
|
|
} while (0)
|
|
#elif defined(__arm__)
|
|
/* Note: assumes arm EABI, not OABI */
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARM
|
|
# define sudo_pt_regs struct pt_regs
|
|
# define reg_syscall(x) (x).ARM_r7
|
|
# define reg_retval(x) (x).ARM_r0
|
|
# define reg_sp(x) (x).ARM_sp
|
|
# define reg_arg1(x) (x).ARM_r0
|
|
# define reg_arg2(x) (x).ARM_r1
|
|
# define reg_arg3(x) (x).ARM_r2
|
|
# define reg_arg4(x) (x).ARM_r3
|
|
# define reg_set_syscall(_r, _nr) do { \
|
|
ptrace(PTRACE_SET_SYSCALL, pid, NULL, _nr); \
|
|
} while (0)
|
|
#elif defined (__hppa__)
|
|
/* Untested (should also support hppa64) */
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PARISC
|
|
# define sudo_pt_regs struct user_regs_struct
|
|
# define reg_syscall(x) (x).gr[20] /* r20 */
|
|
# define reg_retval(x) (x).gr[28] /* r28 */
|
|
# define reg_sp(x) (x).gr[30] /* r30 */
|
|
# define reg_arg1(x) (x).gr[26] /* r26 */
|
|
# define reg_arg2(x) (x).gr[25] /* r25 */
|
|
# define reg_arg3(x) (x).gr[24] /* r24 */
|
|
# define reg_arg4(x) (x).gr[23] /* r23 */
|
|
#elif defined(__i386__)
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386
|
|
# define sudo_pt_regs struct user_regs_struct
|
|
# define reg_syscall(x) (x).orig_eax
|
|
# define reg_retval(x) (x).eax
|
|
# define reg_sp(x) (x).esp
|
|
# define reg_arg1(x) (x).ebx
|
|
# define reg_arg2(x) (x).ecx
|
|
# define reg_arg3(x) (x).edx
|
|
# define reg_arg4(x) (x).esi
|
|
#elif defined(__mips__)
|
|
# if _MIPS_SIM == _MIPS_SIM_ABI32
|
|
# /* Linux o32 style syscalls, 4000-4999. */
|
|
# if BYTE_ORDER == LITTLE_ENDIAN
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL
|
|
# else
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS
|
|
# endif
|
|
# elif _MIPS_SIM == _MIPS_SIM_ABI64
|
|
# /* Linux 64-bit syscalls, 5000-5999. */
|
|
# if BYTE_ORDER == LITTLE_ENDIAN
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL64
|
|
# else
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS64
|
|
# endif
|
|
# elif _MIPS_SIM == _MIPS_SIM_NABI32
|
|
# /* Linux N32 syscalls, 6000-6999. */
|
|
# if BYTE_ORDER == LITTLE_ENDIAN
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL64N32
|
|
# else
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS64N32
|
|
# endif
|
|
# else
|
|
# error "Unsupported MIPS ABI"
|
|
# endif
|
|
/* Untested/incomplete.
|
|
* If called via syscall(__NR_###), v0 holds __NR_O32_Linux and the real
|
|
* syscall the first arg (a0) and other args are shifted by one.
|
|
* We don't currently support this.
|
|
* MIPS may not support setting the system call return via ptrace.
|
|
*/
|
|
# define sudo_pt_regs struct pt_regs
|
|
# define reg_syscall(x) (x).regs[2] /* v0 */
|
|
# define reg_retval(x) (x).regs[2] /* v0 */
|
|
# define reg_sp(x) (x).regs[29] /* sp */
|
|
# define reg_arg1(x) (x).regs[4] /* a0 */
|
|
# define reg_arg2(x) (x).regs[5] /* a1 */
|
|
# define reg_arg3(x) (x).regs[6] /* a2 */
|
|
# define reg_arg4(x) (x).regs[7] /* a3 */
|
|
#elif defined(__powerpc__)
|
|
# if defined(__powerpc64__)
|
|
# if BYTE_ORDER == LITTLE_ENDIAN
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64LE
|
|
# else
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64
|
|
# endif
|
|
# else
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC
|
|
# endif
|
|
# define sudo_pt_regs struct pt_regs
|
|
# define reg_syscall(x) (x).gpr[0] /* r0 */
|
|
# define reg_retval(x) (x).gpr[3] /* r3 */
|
|
# define reg_sp(x) (x).gpr[1] /* r1 */
|
|
# define reg_arg1(x) (x).orig_gpr3 /* r3 */
|
|
# define reg_arg2(x) (x).gpr[4] /* r4 */
|
|
# define reg_arg3(x) (x).gpr[5] /* r5 */
|
|
# define reg_arg4(x) (x).gpr[6] /* r6 */
|
|
# define reg_set_retval(_r, _v) do { \
|
|
if (((_r).trap & 0xfff0) == 0x3000) { \
|
|
/* scv 0 system call, uses negative error code for result. */ \
|
|
reg_retval(_r) = (_v); \
|
|
} else { \
|
|
/* \
|
|
* Set CR0 SO bit to indicate a syscall error, which is stored \
|
|
* as a positive error code. \
|
|
*/ \
|
|
reg_retval(_r) = -(_v); \
|
|
(_r).ccr |= 0x10000000; \
|
|
} \
|
|
} while (0)
|
|
#elif defined(__riscv) && __riscv_xlen == 64
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_RISCV64
|
|
# define sudo_pt_regs struct user_regs_struct
|
|
# define reg_syscall(x) (x).a7
|
|
# define reg_retval(x) (x).a0
|
|
# define reg_sp(x) (x).sp
|
|
# define reg_arg1(x) (x).a0
|
|
# define reg_arg2(x) (x).a1
|
|
# define reg_arg3(x) (x).a2
|
|
# define reg_arg4(x) (x).a3
|
|
#elif defined(__s390x__)
|
|
/* Untested/incomplete.
|
|
* s390x may not support setting the system call return via ptrace.
|
|
*/
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_S390X
|
|
# define sudo_pt_regs s390_regs
|
|
# define reg_syscall(x) (x).gprs[1] /* r1 */
|
|
# define reg_retval(x) (x).gprs[2] /* r2 */
|
|
# define reg_sp(x) (x).gprs[15] /* r15 */
|
|
# define reg_arg1(x) (x).gprs[2] /* r2 */
|
|
# define reg_arg2(x) (x).gprs[3] /* r3 */
|
|
# define reg_arg3(x) (x).gprs[4] /* r4 */
|
|
# define reg_arg4(x) (x).gprs[5] /* r6 */
|
|
#elif defined(__s390__)
|
|
/* Untested/incomplete.
|
|
* s390 may not support setting the system call return via ptrace.
|
|
*/
|
|
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_S390
|
|
# define sudo_pt_regs s390_regs
|
|
# define reg_syscall(x) (x).gprs[1] /* r1 */
|
|
# define reg_retval(x) (x).gprs[2] /* r2 */
|
|
# define reg_sp(x) (x).gprs[15] /* r15 */
|
|
# define reg_arg1(x) (x).gprs[2] /* r2 */
|
|
# define reg_arg2(x) (x).gprs[3] /* r3 */
|
|
# define reg_arg3(x) (x).gprs[4] /* r4 */
|
|
# define reg_arg4(x) (x).gprs[5] /* r6 */
|
|
#else
|
|
# error "Do not know how to find your architecture's registers"
|
|
#endif
|
|
|
|
/*
|
|
* Compat definitions for running 32-bit binaries on 64-bit platforms.
|
|
* We must define the register struct too since there is no way to
|
|
* get it directly from the system headers.
|
|
*
|
|
* The value of SECCOMP_AUDIT_ARCH_COMPAT is used when matching the
|
|
* architecture in the seccomp(2) filter. We can tell when the compat
|
|
* arch matched by inspecting the message returned by PTRACE_GETEVENTMSG.
|
|
*/
|
|
#if defined(__x86_64__)
|
|
struct i386_user_regs_struct {
|
|
unsigned int ebx;
|
|
unsigned int ecx;
|
|
unsigned int edx;
|
|
unsigned int esi;
|
|
unsigned int edi;
|
|
unsigned int ebp;
|
|
unsigned int eax;
|
|
unsigned int xds;
|
|
unsigned int xes;
|
|
unsigned int xfs;
|
|
unsigned int xgs;
|
|
unsigned int orig_eax;
|
|
unsigned int eip;
|
|
unsigned int xcs;
|
|
unsigned int eflags;
|
|
unsigned int esp;
|
|
unsigned int xss;
|
|
};
|
|
# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_I386
|
|
# define COMPAT_execve 11
|
|
# define COMPAT_execveat 358
|
|
# define compat_sudo_pt_regs struct i386_user_regs_struct
|
|
# define compat_reg_syscall(x) (x).orig_eax
|
|
# define compat_reg_retval(x) (x).eax
|
|
# define compat_reg_sp(x) (x).esp
|
|
# define compat_reg_arg1(x) (x).ebx
|
|
# define compat_reg_arg2(x) (x).ecx
|
|
# define compat_reg_arg3(x) (x).edx
|
|
# define compat_reg_arg4(x) (x).esi
|
|
#elif defined(__aarch64__)
|
|
struct arm_pt_regs {
|
|
unsigned int uregs[18];
|
|
};
|
|
# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_ARM
|
|
# define COMPAT_execve 11
|
|
# define COMPAT_execveat 387
|
|
# define compat_sudo_pt_regs struct arm_pt_regs
|
|
# define compat_reg_syscall(x) (x).uregs[7] /* r7 */
|
|
# define compat_reg_retval(x) (x).uregs[0] /* r0 */
|
|
# define compat_reg_sp(x) (x).uregs[13] /* r13 */
|
|
# define compat_reg_arg1(x) (x).uregs[0] /* r0 */
|
|
# define compat_reg_arg2(x) (x).uregs[1] /* r1 */
|
|
# define compat_reg_arg3(x) (x).uregs[2] /* r2 */
|
|
# define compat_reg_arg4(x) (x).uregs[3] /* r3 */
|
|
# define compat_reg_set_syscall(_r, _nr) reg_set_syscall(_r, _nr)
|
|
#elif defined(__powerpc64__)
|
|
struct ppc_pt_regs {
|
|
unsigned int gpr[32];
|
|
unsigned int nip;
|
|
unsigned int msr;
|
|
unsigned int orig_gpr3;
|
|
unsigned int ctr;
|
|
unsigned int link;
|
|
unsigned int xer;
|
|
unsigned int ccr;
|
|
unsigned int mq;
|
|
unsigned int trap;
|
|
unsigned int dar;
|
|
unsigned int dsisr;
|
|
unsigned int result;
|
|
};
|
|
# if BYTE_ORDER == LITTLE_ENDIAN
|
|
/* There is no AUDIT_ARCH_PPCLE define. */
|
|
# define SECCOMP_AUDIT_ARCH_COMPAT (AUDIT_ARCH_PPC|__AUDIT_ARCH_LE)
|
|
# else
|
|
# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_PPC
|
|
# endif
|
|
# define COMPAT_execve __NR_execve
|
|
# define COMPAT_execveat __NR_execveat
|
|
# define compat_sudo_pt_regs struct ppc_pt_regs
|
|
# define compat_reg_syscall(x) (x).gpr[0] /* r0 */
|
|
# define compat_reg_retval(x) (x).gpr[3] /* r3 */
|
|
# define compat_reg_sp(x) (x).gpr[1] /* r1 */
|
|
# define compat_reg_arg1(x) (x).orig_gpr3 /* r3 */
|
|
# define compat_reg_arg2(x) (x).gpr[4] /* r4 */
|
|
# define compat_reg_arg3(x) (x).gpr[5] /* r5 */
|
|
# define compat_reg_arg4(x) (x).gpr[6] /* r6 */
|
|
# define compat_reg_set_retval(_r, _v) reg_set_retval(_r, _v)
|
|
#endif
|
|
|
|
/* Set the syscall number the "normal" way by default. */
|
|
#ifndef reg_set_syscall
|
|
# define reg_set_syscall(_r, _nr) do { \
|
|
reg_syscall(_r) = (_nr); \
|
|
} while (0)
|
|
#endif
|
|
#ifndef compat_reg_set_syscall
|
|
# define compat_reg_set_syscall(_r, _nr) do { \
|
|
compat_reg_syscall(_r) = (_nr); \
|
|
} while (0)
|
|
#endif
|
|
|
|
/* Set the syscall return value the "normal" way by default. */
|
|
#ifndef reg_set_retval
|
|
# define reg_set_retval(_r, _v) do { \
|
|
reg_retval(_r) = (_v); \
|
|
} while (0)
|
|
#endif
|
|
#ifndef compat_reg_set_retval
|
|
# define compat_reg_set_retval(_r, _v) do { \
|
|
compat_reg_retval(_r) = (_v); \
|
|
} while (0)
|
|
#endif
|
|
|
|
struct sudo_ptrace_regs {
|
|
union {
|
|
sudo_pt_regs native;
|
|
#ifdef SECCOMP_AUDIT_ARCH_COMPAT
|
|
compat_sudo_pt_regs compat;
|
|
#endif
|
|
} u;
|
|
unsigned int wordsize;
|
|
bool compat;
|
|
};
|
|
|
|
#endif /* SUDO_EXEC_PTRACE_H */
|