tpws,nfqws: seccomp hardening

This commit is contained in:
bol-van 2022-11-22 17:49:53 +03:00
parent 3f8825e668
commit 7af2d43d5f
23 changed files with 408 additions and 3 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -276,6 +276,8 @@ static int nfq_main()
if (!rawsend_preinit(params.bind_fix4,params.bind_fix6))
goto exiterr;
sec_harden();
if (params.droproot && !droproot(params.uid, params.gid))
goto exiterr;
print_id();

164
nfq/sec.c
View File

@ -8,7 +8,159 @@
#include <grp.h>
#ifdef __linux__
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <syscall.h>
/************ SECCOMP ************/
#ifdef __X32_SYSCALL_BIT
#define X32_SYSCALL_BIT __X32_SYSCALL_BIT
#else
#define X32_SYSCALL_BIT 0x40000000
#endif
// block most of the undesired syscalls to harden against code execution
static long blocked_syscalls[] = {
#ifdef SYS_execv
SYS_execv,
#endif
SYS_execve,SYS_execveat,
#ifdef SYS_exec_with_loader
SYS_exec_with_loader,
#endif
SYS_clone,
#ifdef SYS_clone3
SYS_clone3,
#endif
#ifdef SYS_osf_execve
SYS_osf_execve,
#endif
#ifdef SYS_fork
SYS_fork,
#endif
#ifdef SYS_vfork
SYS_vfork,
#endif
#ifdef SYS_unlink
SYS_unlink,
#endif
SYS_unlinkat,
#ifdef SYS_chmod
SYS_chmod,
#endif
SYS_fchmod,SYS_fchmodat,
#ifdef SYS_chown
SYS_chown,
#endif
#ifdef SYS_chown32
SYS_chown32,
#endif
SYS_fchown,
#ifdef SYS_fchown32
SYS_fchown32,
#endif
#ifdef SYS_lchown
SYS_lchown,
#endif
#ifdef SYS_lchown32
SYS_lchown32,
#endif
SYS_fchownat,
#ifdef SYS_symlink
SYS_symlink,
#endif
SYS_symlinkat,
#ifdef SYS_link
SYS_link,
#endif
SYS_linkat,
SYS_pkey_mprotect,SYS_mprotect,
SYS_truncate,
#ifdef SYS_truncate64
SYS_truncate64,
#endif
SYS_ftruncate,
#ifdef SYS_ftruncate64
SYS_ftruncate64,
#endif
#ifdef SYS_mknod
SYS_mknod,
#endif
SYS_mknodat,
#ifdef SYS_mkdir
SYS_mkdir,
#endif
SYS_mkdirat,
#ifdef SYS_rmdir
SYS_rmdir,
#endif
#ifdef SYS_rename
SYS_rename,
#endif
SYS_renameat,SYS_renameat2
};
#define BLOCKED_SYSCALL_COUNT (sizeof(blocked_syscalls)/sizeof(*blocked_syscalls))
static void set_filter(struct sock_filter *filter, __u16 code, __u8 jt, __u8 jf, __u32 k)
{
filter->code = code;
filter->jt = jt;
filter->jf = jf;
filter->k = k;
}
// deny all blocked syscalls
bool set_seccomp()
{
#define SECCOMP_PROG_SIZE (6 + BLOCKED_SYSCALL_COUNT)
struct sock_fprog prog = { .len = SECCOMP_PROG_SIZE };
int res,i,idx=0;
prog.filter = calloc(SECCOMP_PROG_SIZE, sizeof(*prog.filter));
if (!prog.filter) return false;
set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, arch_nr);
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 0, 3 + BLOCKED_SYSCALL_COUNT, ARCH_NR); // fail
set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_nr);
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JGT + BPF_K, 1 + BLOCKED_SYSCALL_COUNT, 0, X32_SYSCALL_BIT - 1); // fail
/*
// ! THIS IS NOT WORKING BECAUSE perror() in glibc dups() stderr
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 0, 3, SYS_write); // special check for write call
set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_arg(0)); // fd
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JGT + BPF_K, 2 + BLOCKED_SYSCALL_COUNT, 0, 2); // 1 - stdout, 2 - stderr. greater are bad
set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_nr); // reload syscall_nr
*/
for(i=0 ; i<BLOCKED_SYSCALL_COUNT ; i++)
{
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, BLOCKED_SYSCALL_COUNT-i, 0, blocked_syscalls[i]);
}
set_filter(&prog.filter[idx++], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_ALLOW); // success case
set_filter(&prog.filter[idx++], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_KILL); // fail case
res=prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
free(prog.filter);
return res>=0;
}
bool sec_harden()
{
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
{
perror("PR_SET_NO_NEW_PRIVS(prctl)");
return false;
}
#if ARCH_NR!=0
if (!set_seccomp())
{
perror("seccomp");
return false;
}
#endif
return true;
}
bool checkpcap(uint64_t caps)
{
@ -69,7 +221,17 @@ bool dropcaps()
}
return true;
}
#endif
#else // __linux__
bool sec_harden()
{
// noop
return true;
}
#endif // __linux__
bool can_drop_root()
{

View File

@ -5,14 +5,54 @@
#ifdef __linux__
#include <stddef.h>
#include <sys/capability.h>
#include <linux/audit.h>
bool checkpcap(uint64_t caps);
bool setpcap(uint64_t caps);
int getmaxcap();
bool dropcaps();
#define syscall_nr (offsetof(struct seccomp_data, nr))
#define arch_nr (offsetof(struct seccomp_data, arch))
#define syscall_arg(x) (offsetof(struct seccomp_data, args[x]))
#if defined(__aarch64__)
# define REG_SYSCALL regs.regs[8]
# define ARCH_NR AUDIT_ARCH_AARCH64
#elif defined(__amd64__)
# define REG_SYSCALL REG_RAX
# define ARCH_NR AUDIT_ARCH_X86_64
#elif defined(__arm__) && (defined(__ARM_EABI__) || defined(__thumb__))
# define REG_SYSCALL regs.uregs[7]
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define ARCH_NR AUDIT_ARCH_ARM
# else
# define ARCH_NR AUDIT_ARCH_ARMEB
# endif
#elif defined(__i386__)
# define REG_SYSCALL REG_EAX
# define ARCH_NR AUDIT_ARCH_I386
#elif defined(__mips__)
# define REG_SYSCALL regs[2]
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define ARCH_NR AUDIT_ARCH_MIPSEL
# else
# define ARCH_NR AUDIT_ARCH_MIPS
# endif
#elif defined(__PPC__)
# define REG_SYSCALL regs.gpr[0]
# define ARCH_NR AUDIT_ARCH_PPC
#else
# warning "Platform does not support seccomp filter yet"
# define REG_SYSCALL 0
# define ARCH_NR 0
#endif
#endif
bool sec_harden();
bool can_drop_root();
bool droproot(uid_t uid, gid_t gid);
void print_id();

View File

@ -10,6 +10,157 @@
#ifdef __linux__
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <syscall.h>
/************ SECCOMP ************/
#ifdef __X32_SYSCALL_BIT
#define X32_SYSCALL_BIT __X32_SYSCALL_BIT
#else
#define X32_SYSCALL_BIT 0x40000000
#endif
// block most of the undesired syscalls to harden against code execution
static long blocked_syscalls[] = {
#ifdef SYS_execv
SYS_execv,
#endif
SYS_execve,SYS_execveat,
#ifdef SYS_exec_with_loader
SYS_exec_with_loader,
#endif
SYS_clone,
#ifdef SYS_clone3
SYS_clone3,
#endif
#ifdef SYS_osf_execve
SYS_osf_execve,
#endif
#ifdef SYS_fork
SYS_fork,
#endif
#ifdef SYS_vfork
SYS_vfork,
#endif
#ifdef SYS_unlink
SYS_unlink,
#endif
SYS_unlinkat,
#ifdef SYS_chmod
SYS_chmod,
#endif
SYS_fchmod,SYS_fchmodat,
#ifdef SYS_chown
SYS_chown,
#endif
#ifdef SYS_chown32
SYS_chown32,
#endif
SYS_fchown,
#ifdef SYS_fchown32
SYS_fchown32,
#endif
#ifdef SYS_lchown
SYS_lchown,
#endif
#ifdef SYS_lchown32
SYS_lchown32,
#endif
SYS_fchownat,
#ifdef SYS_symlink
SYS_symlink,
#endif
SYS_symlinkat,
#ifdef SYS_link
SYS_link,
#endif
SYS_linkat,
SYS_pkey_mprotect,SYS_mprotect,
SYS_truncate,
#ifdef SYS_truncate64
SYS_truncate64,
#endif
SYS_ftruncate,
#ifdef SYS_ftruncate64
SYS_ftruncate64,
#endif
#ifdef SYS_mknod
SYS_mknod,
#endif
SYS_mknodat,
#ifdef SYS_mkdir
SYS_mkdir,
#endif
SYS_mkdirat,
#ifdef SYS_rmdir
SYS_rmdir,
#endif
#ifdef SYS_rename
SYS_rename,
#endif
SYS_renameat,SYS_renameat2
};
#define BLOCKED_SYSCALL_COUNT (sizeof(blocked_syscalls)/sizeof(*blocked_syscalls))
static void set_filter(struct sock_filter *filter, __u16 code, __u8 jt, __u8 jf, __u32 k)
{
filter->code = code;
filter->jt = jt;
filter->jf = jf;
filter->k = k;
}
// deny all blocked syscalls
bool set_seccomp()
{
#define SECCOMP_PROG_SIZE (6 + BLOCKED_SYSCALL_COUNT)
struct sock_fprog prog = { .len = SECCOMP_PROG_SIZE };
int res,i,idx=0;
prog.filter = calloc(SECCOMP_PROG_SIZE, sizeof(*prog.filter));
if (!prog.filter) return false;
set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, arch_nr);
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 0, 3 + BLOCKED_SYSCALL_COUNT, ARCH_NR); // fail
set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_nr);
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JGT + BPF_K, 1 + BLOCKED_SYSCALL_COUNT, 0, X32_SYSCALL_BIT - 1); // fail
/*
// ! THIS IS NOT WORKING BECAUSE perror() in glibc dups() stderr
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 0, 3, SYS_write); // special check for write call
set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_arg(0)); // fd
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JGT + BPF_K, 2 + BLOCKED_SYSCALL_COUNT, 0, 2); // 1 - stdout, 2 - stderr. greater are bad
set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_nr); // reload syscall_nr
*/
for(i=0 ; i<BLOCKED_SYSCALL_COUNT ; i++)
{
set_filter(&prog.filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, BLOCKED_SYSCALL_COUNT-i, 0, blocked_syscalls[i]);
}
set_filter(&prog.filter[idx++], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_ALLOW); // success case
set_filter(&prog.filter[idx++], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_KILL); // fail case
res=prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
free(prog.filter);
return res>=0;
}
bool sec_harden()
{
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
{
perror("PR_SET_NO_NEW_PRIVS(prctl)");
return false;
}
#if ARCH_NR!=0
if (!set_seccomp())
{
perror("seccomp");
return false;
}
#endif
return true;
}
bool checkpcap(uint64_t caps)
{
@ -70,7 +221,17 @@ bool dropcaps()
}
return true;
}
#endif
#else // __linux__
bool sec_harden()
{
// noop
return true;
}
#endif // __linux__
bool can_drop_root()
{

View File

@ -5,14 +5,54 @@
#ifdef __linux__
#include <stddef.h>
#include <sys/capability.h>
#include <linux/audit.h>
bool checkpcap(uint64_t caps);
bool setpcap(uint64_t caps);
int getmaxcap();
bool dropcaps();
#define syscall_nr (offsetof(struct seccomp_data, nr))
#define arch_nr (offsetof(struct seccomp_data, arch))
#define syscall_arg(x) (offsetof(struct seccomp_data, args[x]))
#if defined(__aarch64__)
# define REG_SYSCALL regs.regs[8]
# define ARCH_NR AUDIT_ARCH_AARCH64
#elif defined(__amd64__)
# define REG_SYSCALL REG_RAX
# define ARCH_NR AUDIT_ARCH_X86_64
#elif defined(__arm__) && (defined(__ARM_EABI__) || defined(__thumb__))
# define REG_SYSCALL regs.uregs[7]
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define ARCH_NR AUDIT_ARCH_ARM
# else
# define ARCH_NR AUDIT_ARCH_ARMEB
# endif
#elif defined(__i386__)
# define REG_SYSCALL REG_EAX
# define ARCH_NR AUDIT_ARCH_I386
#elif defined(__mips__)
# define REG_SYSCALL regs[2]
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define ARCH_NR AUDIT_ARCH_MIPSEL
# else
# define ARCH_NR AUDIT_ARCH_MIPS
# endif
#elif defined(__PPC__)
# define REG_SYSCALL regs.gpr[0]
# define ARCH_NR AUDIT_ARCH_PPC
#else
# warning "Platform does not support seccomp filter yet"
# define REG_SYSCALL 0
# define ARCH_NR 0
#endif
#endif
bool sec_harden();
bool can_drop_root();
bool droproot(uid_t uid, gid_t gid);
void print_id();

View File

@ -943,11 +943,11 @@ int main(int argc, char *argv[])
}
set_ulimit();
sec_harden();
if (params.droproot && !droproot(params.uid,params.gid))
goto exiterr;
print_id();
//splice() causes the process to receive the SIGPIPE-signal if one part (for
//example a socket) is closed during splice(). I would rather have splice()
//fail and return -1, so blocking SIGPIPE.