Adding upstream version 2.13.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
parent
38ef975b7f
commit
1fa764a8d3
1212 changed files with 566468 additions and 0 deletions
696
lib/sandbox.c
Normal file
696
lib/sandbox.c
Normal file
|
@ -0,0 +1,696 @@
|
|||
/*
|
||||
* sandbox.c: Process sandboxing
|
||||
*
|
||||
* Copyright (C) 2017 Colin Watson.
|
||||
*
|
||||
* This file is part of man-db.
|
||||
*
|
||||
* man-db is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* man-db is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with man-db; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Some of the syscall lists in this file come from systemd, whose
|
||||
* copyright/licensing statement is as follows. Per LGPLv2.1 s. 3, I have
|
||||
* altered the original references to LGPLv2.1 to refer to GPLv2 instead.
|
||||
*
|
||||
* Copyright 2014 Lennart Poettering
|
||||
*
|
||||
* systemd is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* systemd is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with systemd; If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_LIBSECCOMP
|
||||
# include <seccomp.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <sys/ipc.h>
|
||||
# include <sys/mman.h>
|
||||
# include <sys/prctl.h>
|
||||
# include <sys/shm.h>
|
||||
# include <sys/socket.h>
|
||||
# include <termios.h>
|
||||
#endif /* HAVE_LIBSECCOMP */
|
||||
|
||||
#include "attribute.h"
|
||||
#include "xalloc.h"
|
||||
#include "xstrndup.h"
|
||||
|
||||
#include "manconfig.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "fatal.h"
|
||||
#include "sandbox.h"
|
||||
|
||||
struct man_sandbox {
|
||||
#ifdef HAVE_LIBSECCOMP
|
||||
scmp_filter_ctx ctx;
|
||||
scmp_filter_ctx permissive_ctx;
|
||||
#else /* !HAVE_LIBSECCOMP */
|
||||
char dummy;
|
||||
#endif /* HAVE_LIBSECCOMP */
|
||||
};
|
||||
|
||||
#ifdef HAVE_LIBSECCOMP
|
||||
static bool seccomp_filter_unavailable = false;
|
||||
|
||||
static void gripe_seccomp_filter_unavailable (void)
|
||||
{
|
||||
debug ("seccomp filtering requires a kernel configured with "
|
||||
"CONFIG_SECCOMP_FILTER\n");
|
||||
}
|
||||
|
||||
static bool search_ld_preload (const char *needle)
|
||||
{
|
||||
const char *ld_preload_env;
|
||||
static char *ld_preload_file = NULL;
|
||||
|
||||
ld_preload_env = getenv ("LD_PRELOAD");
|
||||
if (ld_preload_env && strstr (ld_preload_env, needle) != NULL)
|
||||
return true;
|
||||
|
||||
if (!ld_preload_file) {
|
||||
int fd;
|
||||
struct stat st;
|
||||
char *mapped = NULL;
|
||||
|
||||
fd = open ("/etc/ld.so.preload", O_RDONLY);
|
||||
if (fd >= 0 && fstat (fd, &st) >= 0 && st.st_size)
|
||||
mapped = mmap (NULL, st.st_size, PROT_READ,
|
||||
MAP_PRIVATE | MAP_FILE, fd, 0);
|
||||
if (mapped) {
|
||||
ld_preload_file = xstrndup (mapped, st.st_size);
|
||||
munmap (mapped, st.st_size);
|
||||
} else
|
||||
ld_preload_file = xstrdup ("");
|
||||
if (fd >= 0)
|
||||
close (fd);
|
||||
}
|
||||
/* This isn't very accurate: /etc/ld.so.preload may contain
|
||||
* comments. On the other hand, glibc says "it should only be used
|
||||
* for emergencies and testing". File a bug if this is a problem
|
||||
* for you.
|
||||
*/
|
||||
if (strstr (ld_preload_file, needle) != NULL)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Can we load a seccomp filter into this process?
|
||||
*
|
||||
* This guard allows us to call sandbox_load in code paths that may
|
||||
* conditionally do so again.
|
||||
*/
|
||||
static bool can_load_seccomp (void)
|
||||
{
|
||||
const char *man_disable_seccomp;
|
||||
int seccomp_status;
|
||||
|
||||
if (seccomp_filter_unavailable) {
|
||||
gripe_seccomp_filter_unavailable ();
|
||||
return false;
|
||||
}
|
||||
|
||||
man_disable_seccomp = getenv ("MAN_DISABLE_SECCOMP");
|
||||
if (man_disable_seccomp && *man_disable_seccomp) {
|
||||
debug ("seccomp filter disabled by user request\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Valgrind causes the child process to make some system calls we
|
||||
* don't want to allow in general, so disable seccomp when running
|
||||
* on Valgrind.
|
||||
*
|
||||
* The correct approach seems to be to either require valgrind.h at
|
||||
* build-time or copy valgrind.h into this project and then use the
|
||||
* RUNNING_ON_VALGRIND macro, but I'd really rather not add a
|
||||
* build-dependency for this or take a copy of a >6000-line header
|
||||
* file. Since the goal of this is only to disable the seccomp
|
||||
* filter under Valgrind, this will do for now.
|
||||
*/
|
||||
if (search_ld_preload ("/vgpreload")) {
|
||||
debug ("seccomp filter disabled while running under "
|
||||
"Valgrind\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
seccomp_status = prctl (PR_GET_SECCOMP);
|
||||
|
||||
if (seccomp_status == 0)
|
||||
return true;
|
||||
|
||||
if (seccomp_status == -1) {
|
||||
if (errno == EINVAL)
|
||||
debug ("running kernel does not support seccomp\n");
|
||||
else
|
||||
debug ("unknown error getting seccomp status: %s\n",
|
||||
strerror (errno));
|
||||
} else if (seccomp_status == 2)
|
||||
debug ("seccomp already enabled\n");
|
||||
else
|
||||
debug ("unknown return value from PR_GET_SECCOMP: %d\n",
|
||||
seccomp_status);
|
||||
return false;
|
||||
}
|
||||
#endif /* HAVE_LIBSECCOMP */
|
||||
|
||||
#ifdef HAVE_LIBSECCOMP
|
||||
|
||||
# define SC_ALLOW(name) \
|
||||
do { \
|
||||
int nr = seccomp_syscall_resolve_name (name); \
|
||||
if (nr == __NR_SCMP_ERROR) \
|
||||
break; \
|
||||
if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, nr, 0) < 0) \
|
||||
fatal (errno, "can't add seccomp rule"); \
|
||||
} while (0)
|
||||
|
||||
# define SC_ALLOW_PERMISSIVE(name) \
|
||||
do { \
|
||||
if (permissive) \
|
||||
SC_ALLOW (name); \
|
||||
} while (0)
|
||||
|
||||
# define SC_ALLOW_ARG_1(name, cmp1) \
|
||||
do { \
|
||||
int nr = seccomp_syscall_resolve_name (name); \
|
||||
if (nr == __NR_SCMP_ERROR) \
|
||||
break; \
|
||||
if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, nr, 1, cmp1) < \
|
||||
0) \
|
||||
fatal (errno, "can't add seccomp rule"); \
|
||||
} while (0)
|
||||
|
||||
# define SC_ALLOW_ARG_2(name, cmp1, cmp2) \
|
||||
do { \
|
||||
int nr = seccomp_syscall_resolve_name (name); \
|
||||
if (nr == __NR_SCMP_ERROR) \
|
||||
break; \
|
||||
if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, nr, 2, cmp1, \
|
||||
cmp2) < 0) \
|
||||
fatal (errno, "can't add seccomp rule"); \
|
||||
} while (0)
|
||||
|
||||
/* Create a seccomp filter.
|
||||
*
|
||||
* If permissive is true, then the returned filter will allow limited file
|
||||
* creation (although not making executable files). This obviously
|
||||
* constitutes less effective confinement, but it's necessary for some
|
||||
* subprocesses (such as groff) that need the ability to write to temporary
|
||||
* files. Confining these further requires additional tools that can do
|
||||
* path-based filtering or similar, such as AppArmor.
|
||||
*/
|
||||
static scmp_filter_ctx make_seccomp_filter (bool permissive)
|
||||
{
|
||||
scmp_filter_ctx ctx;
|
||||
mode_t mode_mask = S_ISUID | S_ISGID | S_IXUSR | S_IXGRP | S_IXOTH;
|
||||
int create_mask = O_CREAT
|
||||
# ifdef O_TMPFILE
|
||||
| O_TMPFILE
|
||||
# endif /* O_TMPFILE */
|
||||
;
|
||||
|
||||
if (!can_load_seccomp ())
|
||||
return NULL;
|
||||
|
||||
debug ("initialising seccomp filter (permissive: %d)\n",
|
||||
(int) permissive);
|
||||
ctx = seccomp_init (SCMP_ACT_ERRNO (ENOSYS));
|
||||
if (!ctx)
|
||||
fatal (errno, "can't initialise seccomp filter");
|
||||
|
||||
/* Allow sibling architectures for x86, since people sometimes mix
|
||||
* and match architectures there for performance reasons.
|
||||
*/
|
||||
switch (seccomp_arch_native ()) {
|
||||
case SCMP_ARCH_X86:
|
||||
seccomp_arch_add (ctx, SCMP_ARCH_X86_64);
|
||||
seccomp_arch_add (ctx, SCMP_ARCH_X32);
|
||||
break;
|
||||
case SCMP_ARCH_X86_64:
|
||||
seccomp_arch_add (ctx, SCMP_ARCH_X86);
|
||||
seccomp_arch_add (ctx, SCMP_ARCH_X32);
|
||||
break;
|
||||
case SCMP_ARCH_X32:
|
||||
seccomp_arch_add (ctx, SCMP_ARCH_X86);
|
||||
seccomp_arch_add (ctx, SCMP_ARCH_X86_64);
|
||||
break;
|
||||
}
|
||||
|
||||
/* This sandbox is intended to allow operations that might
|
||||
* reasonably be needed in simple data-transforming pipes: it should
|
||||
* allow the process to do most reasonable things to itself, to read
|
||||
* and write data from and to already-open file descriptors, to open
|
||||
* files in read-only mode, and to fork new processes with the same
|
||||
* restrictions. (If permissive is true, then it should also allow
|
||||
* limited file creation; see the header comment above.)
|
||||
*
|
||||
* Since I currently know of no library with suitable syscall lists,
|
||||
* the syscall lists here are taken from
|
||||
* systemd:src/shared/seccomp-util.c, last updated from commit
|
||||
* 85b774de49caff8be819f021740dd680ba76ab77 (2024-03-03).
|
||||
*/
|
||||
|
||||
/* systemd: SystemCallFilter=@default */
|
||||
SC_ALLOW ("arch_prctl");
|
||||
SC_ALLOW ("brk");
|
||||
SC_ALLOW ("cacheflush");
|
||||
SC_ALLOW ("clock_getres");
|
||||
SC_ALLOW ("clock_getres_time64");
|
||||
SC_ALLOW ("clock_gettime");
|
||||
SC_ALLOW ("clock_gettime64");
|
||||
SC_ALLOW ("clock_nanosleep");
|
||||
SC_ALLOW ("clock_nanosleep_time64");
|
||||
SC_ALLOW ("execve");
|
||||
SC_ALLOW ("exit");
|
||||
SC_ALLOW ("exit_group");
|
||||
SC_ALLOW ("futex");
|
||||
SC_ALLOW ("futex_time64");
|
||||
SC_ALLOW ("futex_waitv");
|
||||
SC_ALLOW ("get_robust_list");
|
||||
SC_ALLOW ("get_thread_area");
|
||||
SC_ALLOW ("getegid");
|
||||
SC_ALLOW ("getegid32");
|
||||
SC_ALLOW ("geteuid");
|
||||
SC_ALLOW ("geteuid32");
|
||||
SC_ALLOW ("getgid");
|
||||
SC_ALLOW ("getgid32");
|
||||
SC_ALLOW ("getgroups");
|
||||
SC_ALLOW ("getgroups32");
|
||||
SC_ALLOW ("getpgid");
|
||||
SC_ALLOW ("getpgrp");
|
||||
SC_ALLOW ("getpid");
|
||||
SC_ALLOW ("getppid");
|
||||
SC_ALLOW ("getrandom");
|
||||
SC_ALLOW ("getresgid");
|
||||
SC_ALLOW ("getresgid32");
|
||||
SC_ALLOW ("getresuid");
|
||||
SC_ALLOW ("getresuid32");
|
||||
SC_ALLOW ("getrlimit");
|
||||
SC_ALLOW ("getsid");
|
||||
SC_ALLOW ("gettid");
|
||||
SC_ALLOW ("gettimeofday");
|
||||
SC_ALLOW ("getuid");
|
||||
SC_ALLOW ("getuid32");
|
||||
SC_ALLOW ("membarrier");
|
||||
SC_ALLOW ("mmap");
|
||||
SC_ALLOW ("mmap2");
|
||||
SC_ALLOW ("mprotect");
|
||||
SC_ALLOW ("munmap");
|
||||
SC_ALLOW ("nanosleep");
|
||||
SC_ALLOW ("pause");
|
||||
SC_ALLOW ("prlimit64");
|
||||
SC_ALLOW ("restart_syscall");
|
||||
SC_ALLOW ("riscv_flush_icache");
|
||||
SC_ALLOW ("riscv_hwprobe");
|
||||
SC_ALLOW ("rseq");
|
||||
SC_ALLOW ("rt_sigreturn");
|
||||
SC_ALLOW ("sched_getaffinity");
|
||||
SC_ALLOW ("sched_yield");
|
||||
SC_ALLOW ("set_robust_list");
|
||||
SC_ALLOW ("set_thread_area");
|
||||
SC_ALLOW ("set_tid_address");
|
||||
SC_ALLOW ("set_tls");
|
||||
SC_ALLOW ("sigreturn");
|
||||
SC_ALLOW ("time");
|
||||
SC_ALLOW ("ugetrlimit");
|
||||
|
||||
/* systemd: SystemCallFilter=@basic-io */
|
||||
SC_ALLOW ("_llseek");
|
||||
SC_ALLOW ("close");
|
||||
SC_ALLOW ("close_range");
|
||||
SC_ALLOW ("dup");
|
||||
SC_ALLOW ("dup2");
|
||||
SC_ALLOW ("dup3");
|
||||
SC_ALLOW ("lseek");
|
||||
SC_ALLOW ("pread64");
|
||||
SC_ALLOW ("preadv");
|
||||
SC_ALLOW ("preadv2");
|
||||
SC_ALLOW ("pwrite64");
|
||||
SC_ALLOW ("pwritev");
|
||||
SC_ALLOW ("pwritev2");
|
||||
SC_ALLOW ("read");
|
||||
SC_ALLOW ("readv");
|
||||
SC_ALLOW ("write");
|
||||
SC_ALLOW ("writev");
|
||||
|
||||
/* systemd: SystemCallFilter=@file-system (subset) */
|
||||
SC_ALLOW ("access");
|
||||
SC_ALLOW ("chdir");
|
||||
if (permissive) {
|
||||
SC_ALLOW_ARG_1 ("chmod",
|
||||
SCMP_A1 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
SC_ALLOW_ARG_1 ("creat",
|
||||
SCMP_A1 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
}
|
||||
SC_ALLOW ("faccessat");
|
||||
SC_ALLOW ("faccessat2");
|
||||
SC_ALLOW ("fallocate");
|
||||
SC_ALLOW ("fchdir");
|
||||
if (permissive) {
|
||||
SC_ALLOW_ARG_1 ("fchmod",
|
||||
SCMP_A1 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
SC_ALLOW_ARG_1 ("fchmodat",
|
||||
SCMP_A2 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
SC_ALLOW_ARG_1 ("fchmodat2",
|
||||
SCMP_A2 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
}
|
||||
SC_ALLOW ("fcntl");
|
||||
SC_ALLOW ("fcntl64");
|
||||
SC_ALLOW ("fstat");
|
||||
SC_ALLOW ("fstat64");
|
||||
SC_ALLOW ("fstatat64");
|
||||
SC_ALLOW ("fstatfs");
|
||||
SC_ALLOW ("fstatfs64");
|
||||
SC_ALLOW ("ftruncate");
|
||||
SC_ALLOW ("ftruncate64");
|
||||
SC_ALLOW_PERMISSIVE ("futimesat");
|
||||
SC_ALLOW ("getcwd");
|
||||
SC_ALLOW ("getdents");
|
||||
SC_ALLOW ("getdents64");
|
||||
SC_ALLOW_PERMISSIVE ("link");
|
||||
SC_ALLOW_PERMISSIVE ("linkat");
|
||||
SC_ALLOW ("lstat");
|
||||
SC_ALLOW ("lstat64");
|
||||
SC_ALLOW_PERMISSIVE ("mkdir");
|
||||
SC_ALLOW_PERMISSIVE ("mkdirat");
|
||||
SC_ALLOW ("newfstatat");
|
||||
SC_ALLOW ("oldfstat");
|
||||
SC_ALLOW ("oldlstat");
|
||||
SC_ALLOW ("oldstat");
|
||||
if (permissive) {
|
||||
SC_ALLOW_ARG_2 ("open",
|
||||
SCMP_A1 (SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT),
|
||||
SCMP_A2 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
SC_ALLOW_ARG_2 ("openat",
|
||||
SCMP_A2 (SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT),
|
||||
SCMP_A3 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
# ifdef O_TMPFILE
|
||||
SC_ALLOW_ARG_2 (
|
||||
"open",
|
||||
SCMP_A1 (SCMP_CMP_MASKED_EQ, O_TMPFILE, O_TMPFILE),
|
||||
SCMP_A2 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
SC_ALLOW_ARG_2 (
|
||||
"openat",
|
||||
SCMP_A2 (SCMP_CMP_MASKED_EQ, O_TMPFILE, O_TMPFILE),
|
||||
SCMP_A3 (SCMP_CMP_MASKED_EQ, mode_mask, 0));
|
||||
# endif /* O_TMPFILE */
|
||||
SC_ALLOW_ARG_1 ("open",
|
||||
SCMP_A1 (SCMP_CMP_MASKED_EQ, create_mask, 0));
|
||||
SC_ALLOW_ARG_1 ("openat",
|
||||
SCMP_A2 (SCMP_CMP_MASKED_EQ, create_mask, 0));
|
||||
} else {
|
||||
SC_ALLOW_ARG_1 ("open", SCMP_A1 (SCMP_CMP_MASKED_EQ, O_ACCMODE,
|
||||
O_RDONLY));
|
||||
SC_ALLOW_ARG_1 ("openat", SCMP_A2 (SCMP_CMP_MASKED_EQ,
|
||||
O_ACCMODE, O_RDONLY));
|
||||
}
|
||||
SC_ALLOW ("readlink");
|
||||
SC_ALLOW ("readlinkat");
|
||||
SC_ALLOW_PERMISSIVE ("rename");
|
||||
SC_ALLOW_PERMISSIVE ("renameat");
|
||||
SC_ALLOW_PERMISSIVE ("renameat2");
|
||||
SC_ALLOW_PERMISSIVE ("rmdir");
|
||||
SC_ALLOW ("stat");
|
||||
SC_ALLOW ("stat64");
|
||||
SC_ALLOW ("statfs");
|
||||
SC_ALLOW ("statfs64");
|
||||
SC_ALLOW ("statx");
|
||||
SC_ALLOW_PERMISSIVE ("symlink");
|
||||
SC_ALLOW_PERMISSIVE ("symlinkat");
|
||||
SC_ALLOW_PERMISSIVE ("truncate");
|
||||
SC_ALLOW_PERMISSIVE ("truncateat");
|
||||
SC_ALLOW_PERMISSIVE ("unlink");
|
||||
SC_ALLOW_PERMISSIVE ("unlinkat");
|
||||
SC_ALLOW_PERMISSIVE ("utime");
|
||||
SC_ALLOW_PERMISSIVE ("utimensat");
|
||||
SC_ALLOW_PERMISSIVE ("utimensat_time64");
|
||||
SC_ALLOW_PERMISSIVE ("utimes");
|
||||
|
||||
/* systemd: SystemCallFilter=@io-event */
|
||||
SC_ALLOW ("_newselect");
|
||||
SC_ALLOW ("epoll_create");
|
||||
SC_ALLOW ("epoll_create1");
|
||||
SC_ALLOW ("epoll_ctl");
|
||||
SC_ALLOW ("epoll_ctl_old");
|
||||
SC_ALLOW ("epoll_pwait");
|
||||
SC_ALLOW ("epoll_pwait2");
|
||||
SC_ALLOW ("epoll_wait");
|
||||
SC_ALLOW ("epoll_wait_old");
|
||||
SC_ALLOW ("eventfd");
|
||||
SC_ALLOW ("eventfd2");
|
||||
SC_ALLOW ("poll");
|
||||
SC_ALLOW ("ppoll");
|
||||
SC_ALLOW ("ppoll_time64");
|
||||
SC_ALLOW ("pselect6");
|
||||
SC_ALLOW ("pselect6_time64");
|
||||
SC_ALLOW ("select");
|
||||
|
||||
/* systemd: SystemCallFilter=@ipc (subset) */
|
||||
SC_ALLOW ("pipe");
|
||||
SC_ALLOW ("pipe2");
|
||||
|
||||
/* systemd: SystemCallFilter=@process (subset) */
|
||||
SC_ALLOW ("capget");
|
||||
SC_ALLOW ("clone");
|
||||
SC_ALLOW ("clone3");
|
||||
SC_ALLOW ("execveat");
|
||||
SC_ALLOW ("fork");
|
||||
SC_ALLOW ("getrusage");
|
||||
SC_ALLOW ("pidfd_open");
|
||||
SC_ALLOW ("pidfd_send_signal");
|
||||
SC_ALLOW ("prctl");
|
||||
SC_ALLOW ("vfork");
|
||||
SC_ALLOW ("wait4");
|
||||
SC_ALLOW ("waitid");
|
||||
SC_ALLOW ("waitpid");
|
||||
|
||||
/* systemd: SystemCallFilter=@signal */
|
||||
SC_ALLOW ("rt_sigaction");
|
||||
SC_ALLOW ("rt_sigpending");
|
||||
SC_ALLOW ("rt_sigprocmask");
|
||||
SC_ALLOW ("rt_sigsuspend");
|
||||
SC_ALLOW ("rt_sigtimedwait");
|
||||
SC_ALLOW ("rt_sigtimedwait_time64");
|
||||
SC_ALLOW ("sigaction");
|
||||
SC_ALLOW ("sigaltstack");
|
||||
SC_ALLOW ("signal");
|
||||
SC_ALLOW ("signalfd");
|
||||
SC_ALLOW ("signalfd4");
|
||||
SC_ALLOW ("sigpending");
|
||||
SC_ALLOW ("sigprocmask");
|
||||
SC_ALLOW ("sigsuspend");
|
||||
|
||||
/* systemd: SystemCallFilter=@sync */
|
||||
SC_ALLOW ("fdatasync");
|
||||
SC_ALLOW ("fsync");
|
||||
SC_ALLOW ("msync");
|
||||
SC_ALLOW ("sync");
|
||||
SC_ALLOW ("sync_file_range");
|
||||
SC_ALLOW ("sync_file_range2");
|
||||
SC_ALLOW ("syncfs");
|
||||
|
||||
/* systemd: SystemCallFilter=@system-service (subset) */
|
||||
SC_ALLOW ("arm_fadvise64_64");
|
||||
SC_ALLOW ("fadvise64");
|
||||
SC_ALLOW ("fadvise64_64");
|
||||
if (permissive)
|
||||
SC_ALLOW ("ioctl");
|
||||
else {
|
||||
SC_ALLOW_ARG_1 ("ioctl", SCMP_A1 (SCMP_CMP_EQ, TCGETS));
|
||||
SC_ALLOW_ARG_1 ("ioctl", SCMP_A1 (SCMP_CMP_EQ, TIOCGWINSZ));
|
||||
}
|
||||
SC_ALLOW ("madvise");
|
||||
SC_ALLOW ("mremap");
|
||||
SC_ALLOW ("sysinfo");
|
||||
SC_ALLOW ("uname");
|
||||
|
||||
/* Extra syscalls not in any of systemd's sets. */
|
||||
SC_ALLOW ("arm_fadvise64_64");
|
||||
SC_ALLOW ("arm_sync_file_range");
|
||||
|
||||
/* Allow killing processes and threads. This is unfortunate but
|
||||
* unavoidable: groff uses kill to explicitly pass on SIGPIPE to its
|
||||
* child processes, and we can't do any more sophisticated filtering
|
||||
* in seccomp.
|
||||
*/
|
||||
SC_ALLOW ("kill");
|
||||
SC_ALLOW ("tgkill");
|
||||
|
||||
/* Allow some relatively harmless System V shared memory operations.
|
||||
* These seem to be popular among the sort of program that wants to
|
||||
* install itself in /etc/ld.so.preload or similar (e.g. antivirus
|
||||
* programs and VPNs).
|
||||
*/
|
||||
SC_ALLOW_ARG_1 ("shmat", SCMP_A2 (SCMP_CMP_EQ, SHM_RDONLY));
|
||||
SC_ALLOW_ARG_1 ("shmctl", SCMP_A1 (SCMP_CMP_EQ, IPC_STAT));
|
||||
SC_ALLOW ("shmdt");
|
||||
SC_ALLOW ("shmget");
|
||||
|
||||
/* Some antivirus programs use an LD_PRELOAD wrapper that wants to
|
||||
* talk to a private daemon using a Unix-domain socket. We really
|
||||
* don't want to allow these syscalls in general, but if such a
|
||||
* thing is in use we probably have no choice.
|
||||
*
|
||||
* Firebuild is a build accelerator that connects to its supervisor
|
||||
* using a Unix-domain socket.
|
||||
*
|
||||
* snoopy is an execve monitoring tool that may log messages to
|
||||
* /dev/log.
|
||||
*/
|
||||
if (search_ld_preload ("libesets_pac.so") ||
|
||||
search_ld_preload ("libfirebuild.so") ||
|
||||
search_ld_preload ("libscep_pac.so") ||
|
||||
search_ld_preload ("libsnoopy.so")) {
|
||||
SC_ALLOW ("connect");
|
||||
SC_ALLOW ("recvmsg");
|
||||
SC_ALLOW ("sendmsg");
|
||||
SC_ALLOW ("sendto");
|
||||
SC_ALLOW ("setsockopt");
|
||||
SC_ALLOW_ARG_1 ("socket", SCMP_A0 (SCMP_CMP_EQ, AF_UNIX));
|
||||
}
|
||||
/* ESET sends messages to a System V message queue. */
|
||||
if (search_ld_preload ("libesets_pac.so") ||
|
||||
search_ld_preload ("libscep_pac.so")) {
|
||||
SC_ALLOW_ARG_1 ("msgget", SCMP_A1 (SCMP_CMP_EQ, 0));
|
||||
SC_ALLOW ("msgsnd");
|
||||
}
|
||||
|
||||
# if (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 5) || SCMP_VER_MAJOR > 2
|
||||
if (seccomp_attr_set (ctx, SCMP_FLTATR_CTL_OPTIMIZE, 2) < 0)
|
||||
debug ("failed to set SCMP_FLTATR_CTL_OPTIMIZE\n");
|
||||
# endif /* libseccomp >= 2.5 */
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
# undef SC_ALLOW_ARG_2
|
||||
# undef SC_ALLOW_ARG_1
|
||||
# undef SC_ALLOW
|
||||
|
||||
#endif /* HAVE_LIBSECCOMP */
|
||||
|
||||
/* Create a sandbox for processing untrusted data.
|
||||
*
|
||||
* This only sets up data structures; the caller must call sandbox_load to
|
||||
* actually enter the sandbox.
|
||||
*/
|
||||
man_sandbox *sandbox_init (void)
|
||||
{
|
||||
man_sandbox *sandbox = XZALLOC (man_sandbox);
|
||||
|
||||
#ifdef HAVE_LIBSECCOMP
|
||||
sandbox->ctx = make_seccomp_filter (false);
|
||||
sandbox->permissive_ctx = make_seccomp_filter (true);
|
||||
#else /* !HAVE_LIBSECCOMP */
|
||||
sandbox->dummy = 0;
|
||||
#endif /* HAVE_LIBSECCOMP */
|
||||
|
||||
return sandbox;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSECCOMP
|
||||
static void _sandbox_load (man_sandbox *sandbox, bool permissive)
|
||||
{
|
||||
if (can_load_seccomp ()) {
|
||||
scmp_filter_ctx ctx;
|
||||
|
||||
if (permissive)
|
||||
ctx = sandbox->permissive_ctx;
|
||||
else
|
||||
ctx = sandbox->ctx;
|
||||
if (!ctx)
|
||||
return;
|
||||
debug ("loading seccomp filter (permissive: %d)\n",
|
||||
(int) permissive);
|
||||
if (seccomp_load (ctx) < 0) {
|
||||
if (errno == EINVAL || errno == EFAULT) {
|
||||
/* The kernel doesn't give us particularly
|
||||
* fine-grained errors. EINVAL could in
|
||||
* theory be an invalid BPF program, but
|
||||
* it's much more likely that the running
|
||||
* kernel doesn't support seccomp filtering.
|
||||
* EFAULT normally means a programming
|
||||
* error, but it could also be returned here
|
||||
* by some versions of qemu-user
|
||||
* (https://bugs.launchpad.net/bugs/1726394).
|
||||
*/
|
||||
gripe_seccomp_filter_unavailable ();
|
||||
/* Don't try this again. */
|
||||
seccomp_filter_unavailable = true;
|
||||
} else
|
||||
fatal (errno, "can't load seccomp filter");
|
||||
}
|
||||
}
|
||||
}
|
||||
#else /* !HAVE_LIBSECCOMP */
|
||||
static void _sandbox_load (man_sandbox *sandbox MAYBE_UNUSED,
|
||||
bool permissive MAYBE_UNUSED)
|
||||
{
|
||||
}
|
||||
#endif /* HAVE_LIBSECCOMP */
|
||||
|
||||
/* Enter a sandbox for processing untrusted data. */
|
||||
void sandbox_load (void *data)
|
||||
{
|
||||
man_sandbox *sandbox = data;
|
||||
|
||||
_sandbox_load (sandbox, false);
|
||||
}
|
||||
|
||||
/* Enter a sandbox for processing untrusted data, allowing limited file
|
||||
* creation.
|
||||
*/
|
||||
void sandbox_load_permissive (void *data)
|
||||
{
|
||||
man_sandbox *sandbox = data;
|
||||
|
||||
_sandbox_load (sandbox, true);
|
||||
}
|
||||
|
||||
/* Free a sandbox for processing untrusted data. */
|
||||
void sandbox_free (void *data)
|
||||
{
|
||||
man_sandbox *sandbox = data;
|
||||
|
||||
#ifdef HAVE_LIBSECCOMP
|
||||
if (sandbox->ctx)
|
||||
seccomp_release (sandbox->ctx);
|
||||
if (sandbox->permissive_ctx)
|
||||
seccomp_release (sandbox->permissive_ctx);
|
||||
#endif /* HAVE_LIBSECCOMP */
|
||||
|
||||
free (sandbox);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue