diff options
| -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 | |||
| 306 | # Seccomp filter files. | 306 | # Seccomp filter files. |
| 307 | lib-src/seccomp-filter.bpf | 307 | lib-src/seccomp-filter.bpf |
| 308 | lib-src/seccomp-filter.pfc | 308 | lib-src/seccomp-filter.pfc |
| 309 | lib-src/seccomp-filter-exec.bpf | ||
| 310 | 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 | |||
| 240 | all: ${EXE_FILES} ${SCRIPTS} | 240 | all: ${EXE_FILES} ${SCRIPTS} |
| 241 | 241 | ||
| 242 | ifeq ($(SECCOMP_FILTER),1) | 242 | ifeq ($(SECCOMP_FILTER),1) |
| 243 | all: seccomp-filter.bpf | 243 | all: seccomp-filter.bpf seccomp-filter-exec.bpf |
| 244 | endif | 244 | endif |
| 245 | 245 | ||
| 246 | .PHONY: all need-blessmail maybe-blessmail | 246 | .PHONY: all need-blessmail maybe-blessmail |
| @@ -430,9 +430,10 @@ seccomp-filter$(EXEEXT): $(srcdir)/seccomp-filter.c $(config_h) | |||
| 430 | $(AM_V_CCLD)$(CC) $(ALL_CFLAGS) $(LIBSECCOMP_CFLAGS) $< \ | 430 | $(AM_V_CCLD)$(CC) $(ALL_CFLAGS) $(LIBSECCOMP_CFLAGS) $< \ |
| 431 | $(LIBSECCOMP_LIBS) -o $@ | 431 | $(LIBSECCOMP_LIBS) -o $@ |
| 432 | 432 | ||
| 433 | seccomp-filter.bpf seccomp-filter.pfc: seccomp-filter$(EXEEXT) | 433 | seccomp-filter.bpf seccomp-filter.pfc seccomp-filter-exec.bpf seccomp-filter-exec.pfc: seccomp-filter$(EXEEXT) |
| 434 | $(AM_V_GEN)./seccomp-filter$(EXEEXT) \ | 434 | $(AM_V_GEN)./seccomp-filter$(EXEEXT) \ |
| 435 | seccomp-filter.bpf seccomp-filter.pfc | 435 | seccomp-filter.bpf seccomp-filter.pfc \ |
| 436 | seccomp-filter-exec.bpf seccomp-filter-exec.pfc | ||
| 436 | endif | 437 | endif |
| 437 | 438 | ||
| 438 | ## Makefile ends here. | 439 | ## 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. | |||
| 26 | 26 | ||
| 27 | Usage: | 27 | Usage: |
| 28 | 28 | ||
| 29 | seccomp-filter out.bpf out.pfc | 29 | seccomp-filter out.bpf out.pfc out-exec.bpf out-exec.pfc |
| 30 | 30 | ||
| 31 | This writes the raw `struct sock_filter' array to out.bpf and a | 31 | This writes the raw `struct sock_filter' array to out.bpf and a |
| 32 | human-readable representation to out.pfc. */ | 32 | human-readable representation to out.pfc. Additionally, it writes |
| 33 | variants of those files that can be used to sandbox Emacs before | ||
| 34 | 'execve' to out-exec.bpf and out-exec.pfc. */ | ||
| 33 | 35 | ||
| 34 | #include "config.h" | 36 | #include "config.h" |
| 35 | 37 | ||
| @@ -42,6 +44,7 @@ human-readable representation to out.pfc. */ | |||
| 42 | #include <stdio.h> | 44 | #include <stdio.h> |
| 43 | #include <time.h> | 45 | #include <time.h> |
| 44 | 46 | ||
| 47 | #include <asm/prctl.h> | ||
| 45 | #include <sys/ioctl.h> | 48 | #include <sys/ioctl.h> |
| 46 | #include <sys/mman.h> | 49 | #include <sys/mman.h> |
| 47 | #include <sys/prctl.h> | 50 | #include <sys/prctl.h> |
| @@ -139,8 +142,9 @@ export_filter (const char *file, | |||
| 139 | int | 142 | int |
| 140 | main (int argc, char **argv) | 143 | main (int argc, char **argv) |
| 141 | { | 144 | { |
| 142 | if (argc != 3) | 145 | if (argc != 5) |
| 143 | fail (0, "usage: %s out.bpf out.pfc", argv[0]); | 146 | fail (0, "usage: %s out.bpf out.pfc out-exec.bpf out-exec.pfc", |
| 147 | argv[0]); | ||
| 144 | 148 | ||
| 145 | /* Any unhandled syscall should abort the Emacs process. */ | 149 | /* Any unhandled syscall should abort the Emacs process. */ |
| 146 | ctx = seccomp_init (SCMP_ACT_KILL_PROCESS); | 150 | ctx = seccomp_init (SCMP_ACT_KILL_PROCESS); |
| @@ -156,6 +160,8 @@ main (int argc, char **argv) | |||
| 156 | verify (CHAR_BIT == 8); | 160 | verify (CHAR_BIT == 8); |
| 157 | verify (sizeof (int) == 4 && INT_MIN == INT32_MIN | 161 | verify (sizeof (int) == 4 && INT_MIN == INT32_MIN |
| 158 | && INT_MAX == INT32_MAX); | 162 | && INT_MAX == INT32_MAX); |
| 163 | verify (sizeof (long) == 8 && LONG_MIN == INT64_MIN | ||
| 164 | && LONG_MAX == INT64_MAX); | ||
| 159 | verify (sizeof (void *) == 8); | 165 | verify (sizeof (void *) == 8); |
| 160 | verify ((uintptr_t) NULL == 0); | 166 | verify ((uintptr_t) NULL == 0); |
| 161 | 167 | ||
| @@ -327,4 +333,29 @@ main (int argc, char **argv) | |||
| 327 | 333 | ||
| 328 | EXPORT_FILTER (argv[1], seccomp_export_bpf); | 334 | EXPORT_FILTER (argv[1], seccomp_export_bpf); |
| 329 | EXPORT_FILTER (argv[2], seccomp_export_pfc); | 335 | EXPORT_FILTER (argv[2], seccomp_export_pfc); |
| 336 | |||
| 337 | /* When applying a Seccomp filter before executing the Emacs binary | ||
| 338 | (e.g. using the `bwrap' program), we need to allow further system | ||
| 339 | calls. Firstly, the wrapper binary will need to `execve' the | ||
| 340 | Emacs binary. Furthermore, the C library requires some system | ||
| 341 | calls at startup time to set up thread-local storage. */ | ||
| 342 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (execve)); | ||
| 343 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (set_tid_address)); | ||
| 344 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (arch_prctl), | ||
| 345 | SCMP_A0_32 (SCMP_CMP_EQ, ARCH_SET_FS)); | ||
| 346 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (statfs)); | ||
| 347 | |||
| 348 | /* We want to allow starting the Emacs binary itself with the | ||
| 349 | --seccomp flag, so we need to allow the `prctl' and `seccomp' | ||
| 350 | system calls. */ | ||
| 351 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (prctl), | ||
| 352 | SCMP_A0_32 (SCMP_CMP_EQ, PR_SET_NO_NEW_PRIVS), | ||
| 353 | SCMP_A1_64 (SCMP_CMP_EQ, 1), SCMP_A2_64 (SCMP_CMP_EQ, 0), | ||
| 354 | SCMP_A3_64 (SCMP_CMP_EQ, 0), SCMP_A4_64 (SCMP_CMP_EQ, 0)); | ||
| 355 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (seccomp), | ||
| 356 | SCMP_A0_32 (SCMP_CMP_EQ, SECCOMP_SET_MODE_FILTER), | ||
| 357 | SCMP_A1_32 (SCMP_CMP_EQ, SECCOMP_FILTER_FLAG_TSYNC)); | ||
| 358 | |||
| 359 | EXPORT_FILTER (argv[3], seccomp_export_bpf); | ||
| 360 | EXPORT_FILTER (argv[4], seccomp_export_pfc); | ||
| 330 | } | 361 | } |
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." | |||
| 177 | (ert-info ((format "Process output: %s" (buffer-string))) | 177 | (ert-info ((format "Process output: %s" (buffer-string))) |
| 178 | (should-not (eql status 0))))))) | 178 | (should-not (eql status 0))))))) |
| 179 | 179 | ||
| 180 | (ert-deftest emacs-tests/bwrap/allows-stdout () | ||
| 181 | (let ((bash (executable-find "bash")) | ||
| 182 | (bwrap (executable-find "bwrap")) | ||
| 183 | (emacs | ||
| 184 | (expand-file-name invocation-name invocation-directory)) | ||
| 185 | (filter (ert-resource-file "seccomp-filter-exec.bpf")) | ||
| 186 | (process-environment nil)) | ||
| 187 | (skip-unless bash) | ||
| 188 | (skip-unless bwrap) | ||
| 189 | (skip-unless (file-executable-p emacs)) | ||
| 190 | (skip-unless (file-readable-p filter)) | ||
| 191 | (should-not (file-remote-p bwrap)) | ||
| 192 | (should-not (file-remote-p emacs)) | ||
| 193 | (should-not (file-remote-p filter)) | ||
| 194 | (with-temp-buffer | ||
| 195 | (let* ((command | ||
| 196 | (concat | ||
| 197 | (mapconcat #'shell-quote-argument | ||
| 198 | `(,(file-name-unquote bwrap) | ||
| 199 | "--ro-bind" "/" "/" | ||
| 200 | "--seccomp" "20" | ||
| 201 | "--" | ||
| 202 | ,(file-name-unquote emacs) | ||
| 203 | "--quick" "--batch" | ||
| 204 | ,(format "--eval=%S" '(message "Hi"))) | ||
| 205 | " ") | ||
| 206 | " 20< " | ||
| 207 | (shell-quote-argument (file-name-unquote filter)))) | ||
| 208 | (status (call-process bash nil t nil "-c" command))) | ||
| 209 | (ert-info ((format "Process output: %s" (buffer-string))) | ||
| 210 | (should (eql status 0))) | ||
| 211 | (should (equal (string-trim (buffer-string)) "Hi")))))) | ||
| 212 | |||
| 180 | ;;; emacs-tests.el ends here | 213 | ;;; emacs-tests.el ends here |