158a0f0d0SEitan Adler /* 258a0f0d0SEitan Adler * Redistribution and use in source and binary forms, with or without 358a0f0d0SEitan Adler * modification, are permitted provided that the following conditions 458a0f0d0SEitan Adler * are met: 558a0f0d0SEitan Adler * 1. Redistributions of source code must retain the above copyright 658a0f0d0SEitan Adler * notice immediately at the beginning of the file, without modification, 758a0f0d0SEitan Adler * this list of conditions, and the following disclaimer. 858a0f0d0SEitan Adler * 2. Redistributions in binary form must reproduce the above copyright 958a0f0d0SEitan Adler * notice, this list of conditions and the following disclaimer in the 1058a0f0d0SEitan Adler * documentation and/or other materials provided with the distribution. 1158a0f0d0SEitan Adler * 1258a0f0d0SEitan Adler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1358a0f0d0SEitan Adler * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1458a0f0d0SEitan Adler * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1558a0f0d0SEitan Adler * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 1658a0f0d0SEitan Adler * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1758a0f0d0SEitan Adler * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1858a0f0d0SEitan Adler * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1958a0f0d0SEitan Adler * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2058a0f0d0SEitan Adler * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2158a0f0d0SEitan Adler * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2258a0f0d0SEitan Adler * SUCH DAMAGE. 2358a0f0d0SEitan Adler */ 2458a0f0d0SEitan Adler /* 2558a0f0d0SEitan Adler * libseccomp hooks. 2658a0f0d0SEitan Adler */ 2758a0f0d0SEitan Adler #include "file.h" 2858a0f0d0SEitan Adler 2958a0f0d0SEitan Adler #ifndef lint 30*a2dfb722SXin LI FILE_RCSID("@(#)$File: seccomp.c,v 1.22 2022/07/30 16:49:18 christos Exp $") 3158a0f0d0SEitan Adler #endif /* lint */ 3258a0f0d0SEitan Adler 3358a0f0d0SEitan Adler #if HAVE_LIBSECCOMP 3458a0f0d0SEitan Adler #include <seccomp.h> /* libseccomp */ 3558a0f0d0SEitan Adler #include <sys/prctl.h> /* prctl */ 36d38c30c0SXin LI #include <sys/ioctl.h> 3758a0f0d0SEitan Adler #include <sys/socket.h> 3843a5ec4eSXin LI #include <termios.h> 3958a0f0d0SEitan Adler #include <fcntl.h> 4058a0f0d0SEitan Adler #include <stdlib.h> 4158a0f0d0SEitan Adler #include <errno.h> 4258a0f0d0SEitan Adler 4358a0f0d0SEitan Adler #define DENY_RULE(call) \ 4458a0f0d0SEitan Adler do \ 4558a0f0d0SEitan Adler if (seccomp_rule_add (ctx, SCMP_ACT_KILL, SCMP_SYS(call), 0) == -1) \ 4658a0f0d0SEitan Adler goto out; \ 4758a0f0d0SEitan Adler while (/*CONSTCOND*/0) 4858a0f0d0SEitan Adler #define ALLOW_RULE(call) \ 4958a0f0d0SEitan Adler do \ 5058a0f0d0SEitan Adler if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(call), 0) == -1) \ 5158a0f0d0SEitan Adler goto out; \ 5258a0f0d0SEitan Adler while (/*CONSTCOND*/0) 5358a0f0d0SEitan Adler 54d38c30c0SXin LI #define ALLOW_IOCTL_RULE(param) \ 55d38c30c0SXin LI do \ 56d38c30c0SXin LI if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, \ 5743a5ec4eSXin LI SCMP_CMP(1, SCMP_CMP_EQ, (scmp_datum_t)param, \ 5843a5ec4eSXin LI (scmp_datum_t)0)) == -1) \ 59d38c30c0SXin LI goto out; \ 60d38c30c0SXin LI while (/*CONSTCOND*/0) 6158a0f0d0SEitan Adler 62d38c30c0SXin LI static scmp_filter_ctx ctx; 6358a0f0d0SEitan Adler 6458a0f0d0SEitan Adler int 6558a0f0d0SEitan Adler enable_sandbox_basic(void) 6658a0f0d0SEitan Adler { 6758a0f0d0SEitan Adler 6858a0f0d0SEitan Adler if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) 6958a0f0d0SEitan Adler return -1; 7058a0f0d0SEitan Adler 7158a0f0d0SEitan Adler if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) 7258a0f0d0SEitan Adler return -1; 7358a0f0d0SEitan Adler 7458a0f0d0SEitan Adler // initialize the filter 7558a0f0d0SEitan Adler ctx = seccomp_init(SCMP_ACT_ALLOW); 7658a0f0d0SEitan Adler if (ctx == NULL) 7758a0f0d0SEitan Adler return 1; 7858a0f0d0SEitan Adler 7958a0f0d0SEitan Adler DENY_RULE(_sysctl); 8058a0f0d0SEitan Adler DENY_RULE(acct); 8158a0f0d0SEitan Adler DENY_RULE(add_key); 8258a0f0d0SEitan Adler DENY_RULE(adjtimex); 8358a0f0d0SEitan Adler DENY_RULE(chroot); 8458a0f0d0SEitan Adler DENY_RULE(clock_adjtime); 8558a0f0d0SEitan Adler DENY_RULE(create_module); 8658a0f0d0SEitan Adler DENY_RULE(delete_module); 8758a0f0d0SEitan Adler DENY_RULE(fanotify_init); 8858a0f0d0SEitan Adler DENY_RULE(finit_module); 8958a0f0d0SEitan Adler DENY_RULE(get_kernel_syms); 9058a0f0d0SEitan Adler DENY_RULE(get_mempolicy); 9158a0f0d0SEitan Adler DENY_RULE(init_module); 9258a0f0d0SEitan Adler DENY_RULE(io_cancel); 9358a0f0d0SEitan Adler DENY_RULE(io_destroy); 9458a0f0d0SEitan Adler DENY_RULE(io_getevents); 9558a0f0d0SEitan Adler DENY_RULE(io_setup); 9658a0f0d0SEitan Adler DENY_RULE(io_submit); 9758a0f0d0SEitan Adler DENY_RULE(ioperm); 9858a0f0d0SEitan Adler DENY_RULE(iopl); 9958a0f0d0SEitan Adler DENY_RULE(ioprio_set); 10058a0f0d0SEitan Adler DENY_RULE(kcmp); 10158a0f0d0SEitan Adler #ifdef __NR_kexec_file_load 10258a0f0d0SEitan Adler DENY_RULE(kexec_file_load); 10358a0f0d0SEitan Adler #endif 10458a0f0d0SEitan Adler DENY_RULE(kexec_load); 10558a0f0d0SEitan Adler DENY_RULE(keyctl); 10658a0f0d0SEitan Adler DENY_RULE(lookup_dcookie); 10758a0f0d0SEitan Adler DENY_RULE(mbind); 10858a0f0d0SEitan Adler DENY_RULE(nfsservctl); 10958a0f0d0SEitan Adler DENY_RULE(migrate_pages); 11058a0f0d0SEitan Adler DENY_RULE(modify_ldt); 11158a0f0d0SEitan Adler DENY_RULE(mount); 11258a0f0d0SEitan Adler DENY_RULE(move_pages); 11358a0f0d0SEitan Adler DENY_RULE(name_to_handle_at); 11458a0f0d0SEitan Adler DENY_RULE(open_by_handle_at); 11558a0f0d0SEitan Adler DENY_RULE(perf_event_open); 11658a0f0d0SEitan Adler DENY_RULE(pivot_root); 11758a0f0d0SEitan Adler DENY_RULE(process_vm_readv); 11858a0f0d0SEitan Adler DENY_RULE(process_vm_writev); 11958a0f0d0SEitan Adler DENY_RULE(ptrace); 12058a0f0d0SEitan Adler DENY_RULE(reboot); 12158a0f0d0SEitan Adler DENY_RULE(remap_file_pages); 12258a0f0d0SEitan Adler DENY_RULE(request_key); 12358a0f0d0SEitan Adler DENY_RULE(set_mempolicy); 12458a0f0d0SEitan Adler DENY_RULE(swapoff); 12558a0f0d0SEitan Adler DENY_RULE(swapon); 12658a0f0d0SEitan Adler DENY_RULE(sysfs); 12758a0f0d0SEitan Adler DENY_RULE(syslog); 12858a0f0d0SEitan Adler DENY_RULE(tuxcall); 12958a0f0d0SEitan Adler DENY_RULE(umount2); 13058a0f0d0SEitan Adler DENY_RULE(uselib); 13158a0f0d0SEitan Adler DENY_RULE(vmsplice); 13258a0f0d0SEitan Adler 13358a0f0d0SEitan Adler // blocking dangerous syscalls that file should not need 13458a0f0d0SEitan Adler DENY_RULE (execve); 13558a0f0d0SEitan Adler DENY_RULE (socket); 13658a0f0d0SEitan Adler // ... 13758a0f0d0SEitan Adler 13858a0f0d0SEitan Adler 13958a0f0d0SEitan Adler // applying filter... 14058a0f0d0SEitan Adler if (seccomp_load (ctx) == -1) 14158a0f0d0SEitan Adler goto out; 14258a0f0d0SEitan Adler // free ctx after the filter has been loaded into the kernel 14358a0f0d0SEitan Adler seccomp_release(ctx); 14458a0f0d0SEitan Adler return 0; 14558a0f0d0SEitan Adler 14658a0f0d0SEitan Adler out: 14758a0f0d0SEitan Adler seccomp_release(ctx); 14858a0f0d0SEitan Adler return -1; 14958a0f0d0SEitan Adler } 15058a0f0d0SEitan Adler 15158a0f0d0SEitan Adler 15258a0f0d0SEitan Adler int 15358a0f0d0SEitan Adler enable_sandbox_full(void) 15458a0f0d0SEitan Adler { 15558a0f0d0SEitan Adler 15658a0f0d0SEitan Adler // prevent child processes from getting more priv e.g. via setuid, 15758a0f0d0SEitan Adler // capabilities, ... 15858a0f0d0SEitan Adler if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) 15958a0f0d0SEitan Adler return -1; 16058a0f0d0SEitan Adler 16158a0f0d0SEitan Adler if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) 16258a0f0d0SEitan Adler return -1; 16358a0f0d0SEitan Adler 16458a0f0d0SEitan Adler // initialize the filter 16558a0f0d0SEitan Adler ctx = seccomp_init(SCMP_ACT_KILL); 16658a0f0d0SEitan Adler if (ctx == NULL) 16758a0f0d0SEitan Adler return -1; 16858a0f0d0SEitan Adler 16958a0f0d0SEitan Adler ALLOW_RULE(access); 17058a0f0d0SEitan Adler ALLOW_RULE(brk); 17158a0f0d0SEitan Adler ALLOW_RULE(close); 17258a0f0d0SEitan Adler ALLOW_RULE(dup2); 17358a0f0d0SEitan Adler ALLOW_RULE(exit); 17458a0f0d0SEitan Adler ALLOW_RULE(exit_group); 17543a5ec4eSXin LI #ifdef __NR_faccessat 17643a5ec4eSXin LI ALLOW_RULE(faccessat); 17743a5ec4eSXin LI #endif 17858a0f0d0SEitan Adler ALLOW_RULE(fcntl); 1792dc4dbb9SEitan Adler ALLOW_RULE(fcntl64); 180*a2dfb722SXin LI #ifdef __NR_fstat 18158a0f0d0SEitan Adler ALLOW_RULE(fstat); 182*a2dfb722SXin LI #endif 1832dc4dbb9SEitan Adler ALLOW_RULE(fstat64); 18443a5ec4eSXin LI #ifdef __NR_fstatat64 18543a5ec4eSXin LI ALLOW_RULE(fstatat64); 186d38c30c0SXin LI #endif 18743a5ec4eSXin LI ALLOW_RULE(futex); 18858a0f0d0SEitan Adler ALLOW_RULE(getdents); 1892dc4dbb9SEitan Adler #ifdef __NR_getdents64 1902dc4dbb9SEitan Adler ALLOW_RULE(getdents64); 1912dc4dbb9SEitan Adler #endif 192d38c30c0SXin LI #ifdef FIONREAD 193d38c30c0SXin LI // called in src/compress.c under sread 194d38c30c0SXin LI ALLOW_IOCTL_RULE(FIONREAD); 195d38c30c0SXin LI #endif 196d38c30c0SXin LI #ifdef TIOCGWINSZ 1972726a701SXin LI // musl libc may call ioctl TIOCGWINSZ on stdout 198d38c30c0SXin LI ALLOW_IOCTL_RULE(TIOCGWINSZ); 199d38c30c0SXin LI #endif 2002726a701SXin LI #ifdef TCGETS 2012726a701SXin LI // glibc may call ioctl TCGETS on stdout on physical terminal 2022726a701SXin LI ALLOW_IOCTL_RULE(TCGETS); 2032726a701SXin LI #endif 20458a0f0d0SEitan Adler ALLOW_RULE(lseek); 2052dc4dbb9SEitan Adler ALLOW_RULE(_llseek); 20658a0f0d0SEitan Adler ALLOW_RULE(lstat); 2072dc4dbb9SEitan Adler ALLOW_RULE(lstat64); 20848c779cdSXin LI ALLOW_RULE(madvise); 20958a0f0d0SEitan Adler ALLOW_RULE(mmap); 2102dc4dbb9SEitan Adler ALLOW_RULE(mmap2); 21158a0f0d0SEitan Adler ALLOW_RULE(mprotect); 21258a0f0d0SEitan Adler ALLOW_RULE(mremap); 21358a0f0d0SEitan Adler ALLOW_RULE(munmap); 2142dc4dbb9SEitan Adler #ifdef __NR_newfstatat 2152dc4dbb9SEitan Adler ALLOW_RULE(newfstatat); 2162dc4dbb9SEitan Adler #endif 21758a0f0d0SEitan Adler ALLOW_RULE(open); 21858a0f0d0SEitan Adler ALLOW_RULE(openat); 21958a0f0d0SEitan Adler ALLOW_RULE(pread64); 22058a0f0d0SEitan Adler ALLOW_RULE(read); 22158a0f0d0SEitan Adler ALLOW_RULE(readlink); 2222726a701SXin LI #ifdef __NR_readlinkat 2232726a701SXin LI ALLOW_RULE(readlinkat); 2242726a701SXin LI #endif 22558a0f0d0SEitan Adler ALLOW_RULE(rt_sigaction); 22658a0f0d0SEitan Adler ALLOW_RULE(rt_sigprocmask); 22758a0f0d0SEitan Adler ALLOW_RULE(rt_sigreturn); 22858a0f0d0SEitan Adler ALLOW_RULE(select); 22958a0f0d0SEitan Adler ALLOW_RULE(stat); 23043a5ec4eSXin LI ALLOW_RULE(statx); 2312dc4dbb9SEitan Adler ALLOW_RULE(stat64); 23258a0f0d0SEitan Adler ALLOW_RULE(sysinfo); 233d38c30c0SXin LI ALLOW_RULE(umask); // Used in file_pipe2file() 2342726a701SXin LI ALLOW_RULE(getpid); // Used by glibc in file_pipe2file() 23558a0f0d0SEitan Adler ALLOW_RULE(unlink); 23658a0f0d0SEitan Adler ALLOW_RULE(write); 23743a5ec4eSXin LI ALLOW_RULE(writev); 23858a0f0d0SEitan Adler 23958a0f0d0SEitan Adler 24058a0f0d0SEitan Adler #if 0 24158a0f0d0SEitan Adler // needed by valgrind 24258a0f0d0SEitan Adler ALLOW_RULE(gettid); 24358a0f0d0SEitan Adler ALLOW_RULE(rt_sigtimedwait); 24458a0f0d0SEitan Adler #endif 24558a0f0d0SEitan Adler 24658a0f0d0SEitan Adler #if 0 24758a0f0d0SEitan Adler /* special restrictions for socket, only allow AF_UNIX/AF_LOCAL */ 24858a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1, 24958a0f0d0SEitan Adler SCMP_CMP(0, SCMP_CMP_EQ, AF_UNIX)) == -1) 25058a0f0d0SEitan Adler goto out; 25158a0f0d0SEitan Adler 25258a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1, 25358a0f0d0SEitan Adler SCMP_CMP(0, SCMP_CMP_EQ, AF_LOCAL)) == -1) 25458a0f0d0SEitan Adler goto out; 25558a0f0d0SEitan Adler 25658a0f0d0SEitan Adler 25758a0f0d0SEitan Adler /* special restrictions for open, prevent opening files for writing */ 25858a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1, 25958a0f0d0SEitan Adler SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY | O_RDWR, 0)) == -1) 26058a0f0d0SEitan Adler goto out; 26158a0f0d0SEitan Adler 26258a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1, 26358a0f0d0SEitan Adler SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY)) == -1) 26458a0f0d0SEitan Adler goto out; 26558a0f0d0SEitan Adler 26658a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1, 26758a0f0d0SEitan Adler SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR)) == -1) 26858a0f0d0SEitan Adler goto out; 26958a0f0d0SEitan Adler 27058a0f0d0SEitan Adler 27158a0f0d0SEitan Adler /* allow stderr */ 27258a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, 27358a0f0d0SEitan Adler SCMP_CMP(0, SCMP_CMP_EQ, 2)) == -1) 27458a0f0d0SEitan Adler goto out; 27558a0f0d0SEitan Adler #endif 27658a0f0d0SEitan Adler 27758a0f0d0SEitan Adler // applying filter... 27858a0f0d0SEitan Adler if (seccomp_load(ctx) == -1) 27958a0f0d0SEitan Adler goto out; 28058a0f0d0SEitan Adler // free ctx after the filter has been loaded into the kernel 28158a0f0d0SEitan Adler seccomp_release(ctx); 28258a0f0d0SEitan Adler return 0; 28358a0f0d0SEitan Adler 28458a0f0d0SEitan Adler out: 28558a0f0d0SEitan Adler // something went wrong 28658a0f0d0SEitan Adler seccomp_release(ctx); 28758a0f0d0SEitan Adler return -1; 28858a0f0d0SEitan Adler } 28958a0f0d0SEitan Adler #endif 290