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*898496eeSXin LI FILE_RCSID("@(#)$File: seccomp.c,v 1.25 2022/12/26 18:57:29 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
enable_sandbox_basic(void)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
enable_sandbox_full(void)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);
180a2dfb722SXin LI #ifdef __NR_fstat
18158a0f0d0SEitan Adler ALLOW_RULE(fstat);
182a2dfb722SXin 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);
236*898496eeSXin LI ALLOW_RULE(utimes);
23758a0f0d0SEitan Adler ALLOW_RULE(write);
23843a5ec4eSXin LI ALLOW_RULE(writev);
23958a0f0d0SEitan Adler
24058a0f0d0SEitan Adler
24158a0f0d0SEitan Adler #if 0
24258a0f0d0SEitan Adler // needed by valgrind
24358a0f0d0SEitan Adler ALLOW_RULE(gettid);
24458a0f0d0SEitan Adler ALLOW_RULE(rt_sigtimedwait);
24558a0f0d0SEitan Adler #endif
24658a0f0d0SEitan Adler
24758a0f0d0SEitan Adler #if 0
24858a0f0d0SEitan Adler /* special restrictions for socket, only allow AF_UNIX/AF_LOCAL */
24958a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
25058a0f0d0SEitan Adler SCMP_CMP(0, SCMP_CMP_EQ, AF_UNIX)) == -1)
25158a0f0d0SEitan Adler goto out;
25258a0f0d0SEitan Adler
25358a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
25458a0f0d0SEitan Adler SCMP_CMP(0, SCMP_CMP_EQ, AF_LOCAL)) == -1)
25558a0f0d0SEitan Adler goto out;
25658a0f0d0SEitan Adler
25758a0f0d0SEitan Adler
25858a0f0d0SEitan Adler /* special restrictions for open, prevent opening files for writing */
25958a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1,
26058a0f0d0SEitan Adler SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY | O_RDWR, 0)) == -1)
26158a0f0d0SEitan Adler goto out;
26258a0f0d0SEitan Adler
26358a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1,
26458a0f0d0SEitan Adler SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY)) == -1)
26558a0f0d0SEitan Adler goto out;
26658a0f0d0SEitan Adler
26758a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1,
26858a0f0d0SEitan Adler SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR)) == -1)
26958a0f0d0SEitan Adler goto out;
27058a0f0d0SEitan Adler
27158a0f0d0SEitan Adler
27258a0f0d0SEitan Adler /* allow stderr */
27358a0f0d0SEitan Adler if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
27458a0f0d0SEitan Adler SCMP_CMP(0, SCMP_CMP_EQ, 2)) == -1)
27558a0f0d0SEitan Adler goto out;
27658a0f0d0SEitan Adler #endif
27758a0f0d0SEitan Adler
27858a0f0d0SEitan Adler // applying filter...
27958a0f0d0SEitan Adler if (seccomp_load(ctx) == -1)
28058a0f0d0SEitan Adler goto out;
28158a0f0d0SEitan Adler // free ctx after the filter has been loaded into the kernel
28258a0f0d0SEitan Adler seccomp_release(ctx);
28358a0f0d0SEitan Adler return 0;
28458a0f0d0SEitan Adler
28558a0f0d0SEitan Adler out:
28658a0f0d0SEitan Adler // something went wrong
28758a0f0d0SEitan Adler seccomp_release(ctx);
28858a0f0d0SEitan Adler return -1;
28958a0f0d0SEitan Adler }
29058a0f0d0SEitan Adler #endif
291