changeset 5461:30f55aaa5fd6 draft

privsep: Add the SECCOMP BPF sandbox for Linux Now we have capsicum, pledge and the POSIX resource limited sandboxes this was quite easy really.
author Roy Marples <roy@marples.name>
date Sat, 19 Sep 2020 20:53:23 +0100
parents 5f999efcfe01
children 6e80b8c6f70c
files src/privsep-linux.c src/privsep.c src/privsep.h
diffstat 3 files changed, 192 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/privsep-linux.c	Sat Sep 19 20:36:07 2020 +0100
+++ b/src/privsep-linux.c	Sat Sep 19 20:53:23 2020 +0100
@@ -27,13 +27,22 @@
  */
 
 #include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/sockios.h>
 
 #include <errno.h>
 #include <fcntl.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
+#include "common.h"
 #include "if.h"
 #include "logerr.h"
 #include "privsep.h"
@@ -87,3 +96,169 @@
 		return -1;
 	return ps_root_readerror(ctx, NULL, 0);
 }
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+# define SECCOMP_ARG_LO	0
+# define SECCOMP_ARG_HI	sizeof(uint32_t)
+#elif (BYTE_ORDER == BIG_ENDIAN)
+# define SECCOMP_ARG_LO	sizeof(uint32_t)
+# define SECCOMP_ARG_HI	0
+#else
+# error "Uknown endian"
+#endif
+
+#define SECCOMP_ALLOW(_nr)						    \
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (_nr), 0, 1),		    \
+	BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
+
+#define SECCOMP_ALLOW_ARG(_nr, _arg, _val)				    \
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (_nr), 0, 6),		    \
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS,				    \
+	    offsetof(struct seccomp_data, args[(_arg)]) + SECCOMP_ARG_LO),  \
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,				    \
+	    ((_val) & 0xffffffff), 0, 3),				    \
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS,				    \
+	    offsetof(struct seccomp_data, args[(_arg)]) + SECCOMP_ARG_HI),  \
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,				    \
+	    (((uint32_t)((uint64_t)(_val) >> 32)) & 0xffffffff), 0, 1),	    \
+	BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),			\
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS,				\
+		offsetof(struct seccomp_data, nr))
+
+#define SECCOMP_FILTER_FAIL	SECCOMP_RET_KILL
+
+#if defined(__i386__)
+#  define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386
+#elif defined(__x86_64__)
+#  define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64
+#elif defined(__arm__)
+#  ifndef EM_ARM
+#    define EM_ARM 40
+#  endif
+#  define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARM
+#elif defined(__aarch64__)
+#  define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64
+#elif defined(__mips__)
+#  if defined(__MIPSEL__)
+#    if defined(__LP64__)
+#      define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL64
+#    else
+#      define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL
+#    endif
+#  elif defined(__LP64__)
+#    define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS64
+#  else
+#    define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS
+#  endif
+#elif defined(__powerpc64__)
+#  define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64
+#elif defined(__powerpc__)
+#  define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC
+#elif defined(__s390x__)
+#  define SECCOMP_AUDIT_ARCH AUDIT_ARCH_S390X
+#elif defined(__s390__)
+#  define SECCOMP_AUDIT_ARCH AUDIT_ARCH_S390
+#elif defined(__sparc__)
+#  if defined(__arch64__)
+#    define AUDIT_ARCH_SPARC64
+#  else
+#    define AUDIT_ARCH_SPARC
+#  endif
+#else
+#  error "Platform does not support seccomp filter yet"
+#endif
+
+static struct sock_filter ps_seccomp_filter[] = {
+	/* Check syscall arch */
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS,
+	    offsetof(struct seccomp_data, arch)),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_AUDIT_ARCH, 1, 0),
+	BPF_STMT(BPF_RET + BPF_K, SECCOMP_FILTER_FAIL),
+	/* Allow syscalls */
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS,
+		offsetof(struct seccomp_data, nr)),
+#ifdef __NR_brk
+	SECCOMP_ALLOW(__NR_brk),
+#endif
+#ifdef __NR_clock_gettime
+	SECCOMP_ALLOW(__NR_clock_gettime),
+#endif
+#if defined(__x86_64__) && defined(__ILP32__) && defined(__X32_SYSCALL_BIT)
+	SECCOMP_ALLOW(__NR_clock_gettime & ~__X32_SYSCALL_BIT),
+#endif
+#ifdef __NR_clock_gettime64
+	SECCOMP_ALLOW(__NR_clock_gettime64),
+#endif
+#ifdef __NR_close
+	SECCOMP_ALLOW(__NR_close),
+#endif
+#ifdef __NR_getpid
+	SECCOMP_ALLOW(__NR_getpid),
+#endif
+#ifdef __NR_ioctl
+	SECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFFLAGS),
+	SECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFHWADDR),
+	SECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFINDEX),
+	SECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFMTU),
+	SECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFVLAN),
+	/* SECCOMP BPF is newer than nl80211 so we don't need SIOCGIWESSID
+	 * which lives in the impossible to include linux/wireless.h header */
+#endif
+#ifdef __NR_ppoll
+	SECCOMP_ALLOW(__NR_ppoll),
+#endif
+#ifdef __NR_ppoll_time64
+	SECCOMP_ALLOW(__NR_ppoll_time64),
+#endif
+#ifdef __NR_read
+	SECCOMP_ALLOW(__NR_read),
+#endif
+#ifdef __NR_readv
+	SECCOMP_ALLOW(__NR_readv),
+#endif
+#ifdef __NR_recvfrom
+	SECCOMP_ALLOW(__NR_recvfrom),
+#endif
+#ifdef __NR_recvmsg
+	SECCOMP_ALLOW(__NR_recvmsg),
+#endif
+#ifdef __NR_rt_sigreturn
+	SECCOMP_ALLOW(__NR_rt_sigreturn),
+#endif
+#ifdef __NR_sendmsg
+	SECCOMP_ALLOW(__NR_sendmsg),
+#endif
+#ifdef __NR_sendto
+	SECCOMP_ALLOW(__NR_sendto),
+#endif
+#ifdef __NR_shutdown
+	SECCOMP_ALLOW(__NR_shutdown),
+#endif
+#ifdef __NR_write
+	SECCOMP_ALLOW(__NR_write),
+#endif
+#ifdef __NR_writev
+	SECCOMP_ALLOW(__NR_writev),
+#endif
+#ifdef __NR_uname
+	SECCOMP_ALLOW(__NR_uname),
+#endif
+	/* Deny everything else */
+	BPF_STMT(BPF_RET + BPF_K, SECCOMP_FILTER_FAIL),
+};
+
+static struct sock_fprog ps_seccomp_prog = {
+	.len = (unsigned short)__arraycount(ps_seccomp_filter),
+	.filter = ps_seccomp_filter,
+};
+
+int
+ps_seccomp_enter(void)
+{
+
+	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
+		return errno == EINVAL ? 0 : -1;
+	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &ps_seccomp_prog) == -1)
+		return errno == EINVAL ? 0 : -1;
+	return 0;
+}
--- a/src/privsep.c	Sat Sep 19 20:36:07 2020 +0100
+++ b/src/privsep.c	Sat Sep 19 20:53:23 2020 +0100
@@ -507,6 +507,12 @@
 #else
 	UNUSED(_pledge);
 #endif
+#ifdef HAVE_SECCOMP
+	if (ps_seccomp_enter() == -1) {
+		logerr("%s: ps_seccomp_enter", __func__);
+		return -1;
+	}
+#endif
 
 	return 0;
 }
--- a/src/privsep.h	Sat Sep 19 20:36:07 2020 +0100
+++ b/src/privsep.h	Sat Sep 19 20:53:23 2020 +0100
@@ -96,6 +96,13 @@
 #define PRIVSEP_RIGHTS
 #endif
 
+#ifdef __linux__
+# include <linux/version.h>
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+#  define HAVE_SECCOMP
+# endif
+#endif
+
 #include "config.h"
 #include "arp.h"
 #include "dhcp.h"
@@ -194,6 +201,10 @@
 int ps_rights_limit_fdpair(int []);
 #endif
 
+#ifdef HAVE_SECCOMP
+int ps_seccomp_enter(void);
+#endif
+
 pid_t ps_dostart(struct dhcpcd_ctx * ctx,
     pid_t *priv_pid, int *priv_fd,
     void (*recv_msg)(void *), void (*recv_unpriv_msg),