xref: /freebsd/contrib/file/src/seccomp.c (revision d38c30c092828f4882ce13b08d0bd3fd6dc7afb5)
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*d38c30c0SXin LI FILE_RCSID("@(#)$File: seccomp.c,v 1.11 2019/07/18 20:32:06 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 */
36*d38c30c0SXin LI #include <sys/ioctl.h>
3758a0f0d0SEitan Adler #include <sys/socket.h>
3858a0f0d0SEitan Adler #include <fcntl.h>
3958a0f0d0SEitan Adler #include <stdlib.h>
4058a0f0d0SEitan Adler #include <errno.h>
4158a0f0d0SEitan Adler 
4258a0f0d0SEitan Adler #define DENY_RULE(call) \
4358a0f0d0SEitan Adler     do \
4458a0f0d0SEitan Adler 	if (seccomp_rule_add (ctx, SCMP_ACT_KILL, SCMP_SYS(call), 0) == -1) \
4558a0f0d0SEitan Adler 	    goto out; \
4658a0f0d0SEitan Adler     while (/*CONSTCOND*/0)
4758a0f0d0SEitan Adler #define ALLOW_RULE(call) \
4858a0f0d0SEitan Adler     do \
4958a0f0d0SEitan Adler 	if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(call), 0) == -1) \
5058a0f0d0SEitan Adler 	    goto out; \
5158a0f0d0SEitan Adler     while (/*CONSTCOND*/0)
5258a0f0d0SEitan Adler 
53*d38c30c0SXin LI #define ALLOW_IOCTL_RULE(param) \
54*d38c30c0SXin LI     do \
55*d38c30c0SXin LI 	if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, \
56*d38c30c0SXin LI 	    SCMP_CMP(1, SCMP_CMP_EQ, param)) == -1) \
57*d38c30c0SXin LI 		goto out; \
58*d38c30c0SXin LI     while (/*CONSTCOND*/0)
5958a0f0d0SEitan Adler 
60*d38c30c0SXin LI static scmp_filter_ctx ctx;
6158a0f0d0SEitan Adler 
6258a0f0d0SEitan Adler int
6358a0f0d0SEitan Adler enable_sandbox_basic(void)
6458a0f0d0SEitan Adler {
6558a0f0d0SEitan Adler 
6658a0f0d0SEitan Adler 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
6758a0f0d0SEitan Adler 		return -1;
6858a0f0d0SEitan Adler 
6958a0f0d0SEitan Adler 	if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1)
7058a0f0d0SEitan Adler 		return -1;
7158a0f0d0SEitan Adler 
7258a0f0d0SEitan Adler 	// initialize the filter
7358a0f0d0SEitan Adler 	ctx = seccomp_init(SCMP_ACT_ALLOW);
7458a0f0d0SEitan Adler 	if (ctx == NULL)
7558a0f0d0SEitan Adler 	    return 1;
7658a0f0d0SEitan Adler 
7758a0f0d0SEitan Adler 	DENY_RULE(_sysctl);
7858a0f0d0SEitan Adler 	DENY_RULE(acct);
7958a0f0d0SEitan Adler 	DENY_RULE(add_key);
8058a0f0d0SEitan Adler 	DENY_RULE(adjtimex);
8158a0f0d0SEitan Adler 	DENY_RULE(chroot);
8258a0f0d0SEitan Adler 	DENY_RULE(clock_adjtime);
8358a0f0d0SEitan Adler 	DENY_RULE(create_module);
8458a0f0d0SEitan Adler 	DENY_RULE(delete_module);
8558a0f0d0SEitan Adler 	DENY_RULE(fanotify_init);
8658a0f0d0SEitan Adler 	DENY_RULE(finit_module);
8758a0f0d0SEitan Adler 	DENY_RULE(get_kernel_syms);
8858a0f0d0SEitan Adler 	DENY_RULE(get_mempolicy);
8958a0f0d0SEitan Adler 	DENY_RULE(init_module);
9058a0f0d0SEitan Adler 	DENY_RULE(io_cancel);
9158a0f0d0SEitan Adler 	DENY_RULE(io_destroy);
9258a0f0d0SEitan Adler 	DENY_RULE(io_getevents);
9358a0f0d0SEitan Adler 	DENY_RULE(io_setup);
9458a0f0d0SEitan Adler 	DENY_RULE(io_submit);
9558a0f0d0SEitan Adler 	DENY_RULE(ioperm);
9658a0f0d0SEitan Adler 	DENY_RULE(iopl);
9758a0f0d0SEitan Adler 	DENY_RULE(ioprio_set);
9858a0f0d0SEitan Adler 	DENY_RULE(kcmp);
9958a0f0d0SEitan Adler #ifdef __NR_kexec_file_load
10058a0f0d0SEitan Adler 	DENY_RULE(kexec_file_load);
10158a0f0d0SEitan Adler #endif
10258a0f0d0SEitan Adler 	DENY_RULE(kexec_load);
10358a0f0d0SEitan Adler 	DENY_RULE(keyctl);
10458a0f0d0SEitan Adler 	DENY_RULE(lookup_dcookie);
10558a0f0d0SEitan Adler 	DENY_RULE(mbind);
10658a0f0d0SEitan Adler 	DENY_RULE(nfsservctl);
10758a0f0d0SEitan Adler 	DENY_RULE(migrate_pages);
10858a0f0d0SEitan Adler 	DENY_RULE(modify_ldt);
10958a0f0d0SEitan Adler 	DENY_RULE(mount);
11058a0f0d0SEitan Adler 	DENY_RULE(move_pages);
11158a0f0d0SEitan Adler 	DENY_RULE(name_to_handle_at);
11258a0f0d0SEitan Adler 	DENY_RULE(open_by_handle_at);
11358a0f0d0SEitan Adler 	DENY_RULE(perf_event_open);
11458a0f0d0SEitan Adler 	DENY_RULE(pivot_root);
11558a0f0d0SEitan Adler 	DENY_RULE(process_vm_readv);
11658a0f0d0SEitan Adler 	DENY_RULE(process_vm_writev);
11758a0f0d0SEitan Adler 	DENY_RULE(ptrace);
11858a0f0d0SEitan Adler 	DENY_RULE(reboot);
11958a0f0d0SEitan Adler 	DENY_RULE(remap_file_pages);
12058a0f0d0SEitan Adler 	DENY_RULE(request_key);
12158a0f0d0SEitan Adler 	DENY_RULE(set_mempolicy);
12258a0f0d0SEitan Adler 	DENY_RULE(swapoff);
12358a0f0d0SEitan Adler 	DENY_RULE(swapon);
12458a0f0d0SEitan Adler 	DENY_RULE(sysfs);
12558a0f0d0SEitan Adler 	DENY_RULE(syslog);
12658a0f0d0SEitan Adler 	DENY_RULE(tuxcall);
12758a0f0d0SEitan Adler 	DENY_RULE(umount2);
12858a0f0d0SEitan Adler 	DENY_RULE(uselib);
12958a0f0d0SEitan Adler 	DENY_RULE(vmsplice);
13058a0f0d0SEitan Adler 
13158a0f0d0SEitan Adler 	// blocking dangerous syscalls that file should not need
13258a0f0d0SEitan Adler 	DENY_RULE (execve);
13358a0f0d0SEitan Adler 	DENY_RULE (socket);
13458a0f0d0SEitan Adler 	// ...
13558a0f0d0SEitan Adler 
13658a0f0d0SEitan Adler 
13758a0f0d0SEitan Adler 	// applying filter...
13858a0f0d0SEitan Adler 	if (seccomp_load (ctx) == -1)
13958a0f0d0SEitan Adler 		goto out;
14058a0f0d0SEitan Adler 	// free ctx after the filter has been loaded into the kernel
14158a0f0d0SEitan Adler 	seccomp_release(ctx);
14258a0f0d0SEitan Adler 	return 0;
14358a0f0d0SEitan Adler 
14458a0f0d0SEitan Adler out:
14558a0f0d0SEitan Adler 	seccomp_release(ctx);
14658a0f0d0SEitan Adler 	return -1;
14758a0f0d0SEitan Adler }
14858a0f0d0SEitan Adler 
14958a0f0d0SEitan Adler 
15058a0f0d0SEitan Adler int
15158a0f0d0SEitan Adler enable_sandbox_full(void)
15258a0f0d0SEitan Adler {
15358a0f0d0SEitan Adler 
15458a0f0d0SEitan Adler 	// prevent child processes from getting more priv e.g. via setuid,
15558a0f0d0SEitan Adler 	// capabilities, ...
15658a0f0d0SEitan Adler 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
15758a0f0d0SEitan Adler 		return -1;
15858a0f0d0SEitan Adler 
15958a0f0d0SEitan Adler 	if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1)
16058a0f0d0SEitan Adler 		return -1;
16158a0f0d0SEitan Adler 
16258a0f0d0SEitan Adler 	// initialize the filter
16358a0f0d0SEitan Adler 	ctx = seccomp_init(SCMP_ACT_KILL);
16458a0f0d0SEitan Adler 	if (ctx == NULL)
16558a0f0d0SEitan Adler 		return -1;
16658a0f0d0SEitan Adler 
16758a0f0d0SEitan Adler 	ALLOW_RULE(access);
16858a0f0d0SEitan Adler 	ALLOW_RULE(brk);
16958a0f0d0SEitan Adler 	ALLOW_RULE(close);
17058a0f0d0SEitan Adler 	ALLOW_RULE(dup2);
17158a0f0d0SEitan Adler 	ALLOW_RULE(exit);
17258a0f0d0SEitan Adler 	ALLOW_RULE(exit_group);
17358a0f0d0SEitan Adler 	ALLOW_RULE(fcntl);
1742dc4dbb9SEitan Adler  	ALLOW_RULE(fcntl64);
17558a0f0d0SEitan Adler 	ALLOW_RULE(fstat);
1762dc4dbb9SEitan Adler  	ALLOW_RULE(fstat64);
177*d38c30c0SXin LI #ifdef XZLIBSUPPORT
178*d38c30c0SXin LI 	ALLOW_RULE(futex);
179*d38c30c0SXin LI #endif
18058a0f0d0SEitan Adler 	ALLOW_RULE(getdents);
1812dc4dbb9SEitan Adler #ifdef __NR_getdents64
1822dc4dbb9SEitan Adler 	ALLOW_RULE(getdents64);
1832dc4dbb9SEitan Adler #endif
184*d38c30c0SXin LI #ifdef FIONREAD
185*d38c30c0SXin LI 	// called in src/compress.c under sread
186*d38c30c0SXin LI 	ALLOW_IOCTL_RULE(FIONREAD);
187*d38c30c0SXin LI #endif
188*d38c30c0SXin LI #ifdef TIOCGWINSZ
189*d38c30c0SXin LI 	// musl libc may call ioctl TIOCGWINSZ when calling stdout
190*d38c30c0SXin LI 	ALLOW_IOCTL_RULE(TIOCGWINSZ);
191*d38c30c0SXin LI #endif
19258a0f0d0SEitan Adler 	ALLOW_RULE(lseek);
1932dc4dbb9SEitan Adler  	ALLOW_RULE(_llseek);
19458a0f0d0SEitan Adler 	ALLOW_RULE(lstat);
1952dc4dbb9SEitan Adler  	ALLOW_RULE(lstat64);
19648c779cdSXin LI 	ALLOW_RULE(madvise);
19758a0f0d0SEitan Adler 	ALLOW_RULE(mmap);
1982dc4dbb9SEitan Adler  	ALLOW_RULE(mmap2);
19958a0f0d0SEitan Adler 	ALLOW_RULE(mprotect);
20058a0f0d0SEitan Adler 	ALLOW_RULE(mremap);
20158a0f0d0SEitan Adler 	ALLOW_RULE(munmap);
2022dc4dbb9SEitan Adler #ifdef __NR_newfstatat
2032dc4dbb9SEitan Adler 	ALLOW_RULE(newfstatat);
2042dc4dbb9SEitan Adler #endif
20558a0f0d0SEitan Adler 	ALLOW_RULE(open);
20658a0f0d0SEitan Adler 	ALLOW_RULE(openat);
20758a0f0d0SEitan Adler 	ALLOW_RULE(pread64);
20858a0f0d0SEitan Adler 	ALLOW_RULE(read);
20958a0f0d0SEitan Adler 	ALLOW_RULE(readlink);
21058a0f0d0SEitan Adler 	ALLOW_RULE(rt_sigaction);
21158a0f0d0SEitan Adler 	ALLOW_RULE(rt_sigprocmask);
21258a0f0d0SEitan Adler 	ALLOW_RULE(rt_sigreturn);
21358a0f0d0SEitan Adler 	ALLOW_RULE(select);
21458a0f0d0SEitan Adler 	ALLOW_RULE(stat);
2152dc4dbb9SEitan Adler 	ALLOW_RULE(stat64);
21658a0f0d0SEitan Adler 	ALLOW_RULE(sysinfo);
217*d38c30c0SXin LI 	ALLOW_RULE(umask);	// Used in file_pipe2file()
21858a0f0d0SEitan Adler 	ALLOW_RULE(unlink);
21958a0f0d0SEitan Adler 	ALLOW_RULE(write);
22058a0f0d0SEitan Adler 
22158a0f0d0SEitan Adler 
22258a0f0d0SEitan Adler #if 0
22358a0f0d0SEitan Adler 	// needed by valgrind
22458a0f0d0SEitan Adler 	ALLOW_RULE(gettid);
22558a0f0d0SEitan Adler 	ALLOW_RULE(getpid);
22658a0f0d0SEitan Adler 	ALLOW_RULE(rt_sigtimedwait);
22758a0f0d0SEitan Adler #endif
22858a0f0d0SEitan Adler 
22958a0f0d0SEitan Adler #if 0
23058a0f0d0SEitan Adler 	 /* special restrictions for socket, only allow AF_UNIX/AF_LOCAL */
23158a0f0d0SEitan Adler 	 if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
23258a0f0d0SEitan Adler 	     SCMP_CMP(0, SCMP_CMP_EQ, AF_UNIX)) == -1)
23358a0f0d0SEitan Adler 	 	goto out;
23458a0f0d0SEitan Adler 
23558a0f0d0SEitan Adler 	 if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
23658a0f0d0SEitan Adler 	     SCMP_CMP(0, SCMP_CMP_EQ, AF_LOCAL)) == -1)
23758a0f0d0SEitan Adler 	 	goto out;
23858a0f0d0SEitan Adler 
23958a0f0d0SEitan Adler 
24058a0f0d0SEitan Adler 	 /* special restrictions for open, prevent opening files for writing */
24158a0f0d0SEitan Adler 	 if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1,
24258a0f0d0SEitan Adler 	     SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY | O_RDWR, 0)) == -1)
24358a0f0d0SEitan Adler 	 	goto out;
24458a0f0d0SEitan Adler 
24558a0f0d0SEitan Adler 	 if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1,
24658a0f0d0SEitan Adler 	     SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY)) == -1)
24758a0f0d0SEitan Adler 	 	goto out;
24858a0f0d0SEitan Adler 
24958a0f0d0SEitan Adler 	 if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1,
25058a0f0d0SEitan Adler 	     SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR)) == -1)
25158a0f0d0SEitan Adler 	 	goto out;
25258a0f0d0SEitan Adler 
25358a0f0d0SEitan Adler 
25458a0f0d0SEitan Adler 	 /* allow stderr */
25558a0f0d0SEitan Adler 	 if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
25658a0f0d0SEitan Adler 	     SCMP_CMP(0, SCMP_CMP_EQ, 2)) == -1)
25758a0f0d0SEitan Adler 		 goto out;
25858a0f0d0SEitan Adler #endif
25958a0f0d0SEitan Adler 
26058a0f0d0SEitan Adler 	// applying filter...
26158a0f0d0SEitan Adler 	if (seccomp_load(ctx) == -1)
26258a0f0d0SEitan Adler 		goto out;
26358a0f0d0SEitan Adler 	// free ctx after the filter has been loaded into the kernel
26458a0f0d0SEitan Adler 	seccomp_release(ctx);
26558a0f0d0SEitan Adler 	return 0;
26658a0f0d0SEitan Adler 
26758a0f0d0SEitan Adler out:
26858a0f0d0SEitan Adler 	// something went wrong
26958a0f0d0SEitan Adler 	seccomp_release(ctx);
27058a0f0d0SEitan Adler 	return -1;
27158a0f0d0SEitan Adler }
27258a0f0d0SEitan Adler #endif
273