#define _GNU_SOURCE #include #include #include "sec.h" #include #include #include #ifdef __linux__ #include #include #include #include // __X32_SYSCALL_BIT defined in linux/unistd.h #include #include #include /************ SECCOMP ************/ // block most of the undesired syscalls to harden against code execution static long blocked_syscalls[] = { #ifdef SYS_execv SYS_execv, #endif SYS_execve, #ifdef SYS_execveat SYS_execveat, #endif #ifdef SYS_exec_with_loader SYS_exec_with_loader, #endif #ifdef SYS_osf_execve SYS_osf_execve, #endif #ifdef SYS_uselib SYS_uselib, #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_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 #ifdef SYS_renameat2 SYS_renameat2, #endif #ifdef SYS_renameat SYS_renameat, #endif #ifdef SYS_readdir SYS_readdir, #endif #ifdef SYS_getdents SYS_getdents, #endif #ifdef SYS_getdents64 SYS_getdents64, #endif #ifdef SYS_process_vm_readv SYS_process_vm_readv, #endif #ifdef SYS_process_vm_writev SYS_process_vm_writev, #endif #ifdef SYS_process_madvise SYS_process_madvise, #endif SYS_kill, SYS_ptrace }; #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 static bool set_seccomp(void) { #ifdef __X32_SYSCALL_BIT #define SECCOMP_PROG_SIZE (6 + BLOCKED_SYSCALL_COUNT) #else #define SECCOMP_PROG_SIZE (5 + BLOCKED_SYSCALL_COUNT) #endif struct sock_filter sockf[SECCOMP_PROG_SIZE]; struct sock_fprog prog = { .len = SECCOMP_PROG_SIZE, .filter = sockf }; int i,idx=0; set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, arch_nr); #ifdef __X32_SYSCALL_BIT // x86 only 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 #else set_filter(&prog.filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 0, 2 + BLOCKED_SYSCALL_COUNT, ARCH_NR); // fail set_filter(&prog.filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_nr); #endif /* // ! 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= 0; } bool sec_harden(void) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { DLOG_PERROR("PR_SET_NO_NEW_PRIVS(prctl)"); return false; } #if ARCH_NR!=0 if (!set_seccomp()) { DLOG_PERROR("seccomp"); if (errno==EINVAL) DLOG_ERR("seccomp: this can be safely ignored if kernel does not support seccomp\n"); return false; } #endif return true; } bool checkpcap(uint64_t caps) { if (!caps) return true; // no special caps reqd struct __user_cap_header_struct ch = {_LINUX_CAPABILITY_VERSION_3, getpid()}; struct __user_cap_data_struct cd[2]; uint32_t c0 = (uint32_t)caps; uint32_t c1 = (uint32_t)(caps>>32); return !capget(&ch,cd) && (cd[0].effective & c0)==c0 && (cd[1].effective & c1)==c1; } bool setpcap(uint64_t caps) { struct __user_cap_header_struct ch = {_LINUX_CAPABILITY_VERSION_3, getpid()}; struct __user_cap_data_struct cd[2]; cd[0].effective = cd[0].permitted = (uint32_t)caps; cd[0].inheritable = 0; cd[1].effective = cd[1].permitted = (uint32_t)(caps>>32); cd[1].inheritable = 0; return !capset(&ch,cd); } int getmaxcap(void) { int maxcap = CAP_LAST_CAP; FILE *F = fopen("/proc/sys/kernel/cap_last_cap", "r"); if (F) { int n = fscanf(F, "%d", &maxcap); fclose(F); } return maxcap; } bool dropcaps(void) { uint64_t caps = 0; int maxcap = getmaxcap(); if (setpcap(caps|(1<