diff options
author | Philipp Stephani <phst@google.com> | 2021-04-11 19:47:36 +0200 |
---|---|---|
committer | Philipp Stephani <phst@google.com> | 2021-04-11 21:19:09 +0200 |
commit | c8d542fd593f06b85d4b7b712378a4f84ec4d2b3 (patch) | |
tree | c2368357e6a417f4d7441e8eec13ee1ffc71b2d2 | |
parent | cf0701eff0f3b06e0324be07f7810cbaf261f7f3 (diff) | |
download | emacs-c8d542fd593f06b85d4b7b712378a4f84ec4d2b3.tar.gz |
Add a variant of the Seccomp filter file that allows 'execve'.
This is useful when starting Emacs with a Seccomp filter enabled,
e.g. using 'bwrap'.
* lib-src/seccomp-filter.c (main): Generate new Seccomp files.
* lib-src/Makefile.in (all)
(seccomp-filter.bpf seccomp-filter.pfc seccomp-filter-exec.bpf
seccomp-filter-exec.pfc): Generate new Seccomp files.
* .gitignore: Ignore new Seccomp files.
* test/src/emacs-tests.el (emacs-tests/bwrap/allows-stdout): New unit
test.
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | lib-src/Makefile.in | 7 | ||||
-rw-r--r-- | lib-src/seccomp-filter.c | 39 | ||||
l--------- | test/src/emacs-resources/seccomp-filter-exec.bpf | 1 | ||||
-rw-r--r-- | test/src/emacs-tests.el | 33 |
5 files changed, 75 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore index ecf768dc4d6..a1e3cb92f87 100644 --- a/.gitignore +++ b/.gitignore @@ -306,3 +306,5 @@ src/gdb.ini # Seccomp filter files. lib-src/seccomp-filter.bpf lib-src/seccomp-filter.pfc +lib-src/seccomp-filter-exec.bpf +lib-src/seccomp-filter-exec.pfc diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in index 35cfa56d8be..091f4fb0199 100644 --- a/lib-src/Makefile.in +++ b/lib-src/Makefile.in @@ -240,7 +240,7 @@ config_h = ../src/config.h $(srcdir)/../src/conf_post.h all: ${EXE_FILES} ${SCRIPTS} ifeq ($(SECCOMP_FILTER),1) -all: seccomp-filter.bpf +all: seccomp-filter.bpf seccomp-filter-exec.bpf endif .PHONY: all need-blessmail maybe-blessmail @@ -430,9 +430,10 @@ seccomp-filter$(EXEEXT): $(srcdir)/seccomp-filter.c $(config_h) $(AM_V_CCLD)$(CC) $(ALL_CFLAGS) $(LIBSECCOMP_CFLAGS) $< \ $(LIBSECCOMP_LIBS) -o $@ -seccomp-filter.bpf seccomp-filter.pfc: seccomp-filter$(EXEEXT) +seccomp-filter.bpf seccomp-filter.pfc seccomp-filter-exec.bpf seccomp-filter-exec.pfc: seccomp-filter$(EXEEXT) $(AM_V_GEN)./seccomp-filter$(EXEEXT) \ - seccomp-filter.bpf seccomp-filter.pfc + seccomp-filter.bpf seccomp-filter.pfc \ + seccomp-filter-exec.bpf seccomp-filter-exec.pfc endif ## Makefile ends here. diff --git a/lib-src/seccomp-filter.c b/lib-src/seccomp-filter.c index a5f2e0adbca..ed362bc18d9 100644 --- a/lib-src/seccomp-filter.c +++ b/lib-src/seccomp-filter.c @@ -26,10 +26,12 @@ only a Linux kernel supporting the Secure Computing extension. Usage: - seccomp-filter out.bpf out.pfc + seccomp-filter out.bpf out.pfc out-exec.bpf out-exec.pfc This writes the raw `struct sock_filter' array to out.bpf and a -human-readable representation to out.pfc. */ +human-readable representation to out.pfc. Additionally, it writes +variants of those files that can be used to sandbox Emacs before +'execve' to out-exec.bpf and out-exec.pfc. */ #include "config.h" @@ -42,6 +44,7 @@ human-readable representation to out.pfc. */ #include <stdio.h> #include <time.h> +#include <asm/prctl.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/prctl.h> @@ -139,8 +142,9 @@ export_filter (const char *file, int main (int argc, char **argv) { - if (argc != 3) - fail (0, "usage: %s out.bpf out.pfc", argv[0]); + if (argc != 5) + fail (0, "usage: %s out.bpf out.pfc out-exec.bpf out-exec.pfc", + argv[0]); /* Any unhandled syscall should abort the Emacs process. */ ctx = seccomp_init (SCMP_ACT_KILL_PROCESS); @@ -156,6 +160,8 @@ main (int argc, char **argv) verify (CHAR_BIT == 8); verify (sizeof (int) == 4 && INT_MIN == INT32_MIN && INT_MAX == INT32_MAX); + verify (sizeof (long) == 8 && LONG_MIN == INT64_MIN + && LONG_MAX == INT64_MAX); verify (sizeof (void *) == 8); verify ((uintptr_t) NULL == 0); @@ -327,4 +333,29 @@ main (int argc, char **argv) EXPORT_FILTER (argv[1], seccomp_export_bpf); EXPORT_FILTER (argv[2], seccomp_export_pfc); + + /* When applying a Seccomp filter before executing the Emacs binary + (e.g. using the `bwrap' program), we need to allow further system + calls. Firstly, the wrapper binary will need to `execve' the + Emacs binary. Furthermore, the C library requires some system + calls at startup time to set up thread-local storage. */ + RULE (SCMP_ACT_ALLOW, SCMP_SYS (execve)); + RULE (SCMP_ACT_ALLOW, SCMP_SYS (set_tid_address)); + RULE (SCMP_ACT_ALLOW, SCMP_SYS (arch_prctl), + SCMP_A0_32 (SCMP_CMP_EQ, ARCH_SET_FS)); + RULE (SCMP_ACT_ALLOW, SCMP_SYS (statfs)); + + /* We want to allow starting the Emacs binary itself with the + --seccomp flag, so we need to allow the `prctl' and `seccomp' + system calls. */ + RULE (SCMP_ACT_ALLOW, SCMP_SYS (prctl), + SCMP_A0_32 (SCMP_CMP_EQ, PR_SET_NO_NEW_PRIVS), + SCMP_A1_64 (SCMP_CMP_EQ, 1), SCMP_A2_64 (SCMP_CMP_EQ, 0), + SCMP_A3_64 (SCMP_CMP_EQ, 0), SCMP_A4_64 (SCMP_CMP_EQ, 0)); + RULE (SCMP_ACT_ALLOW, SCMP_SYS (seccomp), + SCMP_A0_32 (SCMP_CMP_EQ, SECCOMP_SET_MODE_FILTER), + SCMP_A1_32 (SCMP_CMP_EQ, SECCOMP_FILTER_FLAG_TSYNC)); + + EXPORT_FILTER (argv[3], seccomp_export_bpf); + EXPORT_FILTER (argv[4], seccomp_export_pfc); } diff --git a/test/src/emacs-resources/seccomp-filter-exec.bpf b/test/src/emacs-resources/seccomp-filter-exec.bpf new file mode 120000 index 00000000000..5b0e9978221 --- /dev/null +++ b/test/src/emacs-resources/seccomp-filter-exec.bpf @@ -0,0 +1 @@ +../../../lib-src/seccomp-filter-exec.bpf
\ No newline at end of file diff --git a/test/src/emacs-tests.el b/test/src/emacs-tests.el index 89d811f8b4e..09f9a248efb 100644 --- a/test/src/emacs-tests.el +++ b/test/src/emacs-tests.el @@ -177,4 +177,37 @@ to `make-temp-file', which see." (ert-info ((format "Process output: %s" (buffer-string))) (should-not (eql status 0))))))) +(ert-deftest emacs-tests/bwrap/allows-stdout () + (let ((bash (executable-find "bash")) + (bwrap (executable-find "bwrap")) + (emacs + (expand-file-name invocation-name invocation-directory)) + (filter (ert-resource-file "seccomp-filter-exec.bpf")) + (process-environment nil)) + (skip-unless bash) + (skip-unless bwrap) + (skip-unless (file-executable-p emacs)) + (skip-unless (file-readable-p filter)) + (should-not (file-remote-p bwrap)) + (should-not (file-remote-p emacs)) + (should-not (file-remote-p filter)) + (with-temp-buffer + (let* ((command + (concat + (mapconcat #'shell-quote-argument + `(,(file-name-unquote bwrap) + "--ro-bind" "/" "/" + "--seccomp" "20" + "--" + ,(file-name-unquote emacs) + "--quick" "--batch" + ,(format "--eval=%S" '(message "Hi"))) + " ") + " 20< " + (shell-quote-argument (file-name-unquote filter)))) + (status (call-process bash nil t nil "-c" command))) + (ert-info ((format "Process output: %s" (buffer-string))) + (should (eql status 0))) + (should (equal (string-trim (buffer-string)) "Hi")))))) + ;;; emacs-tests.el ends here |