diff options
Diffstat (limited to 'lib-src/seccomp-filter.c')
| -rw-r--r-- | lib-src/seccomp-filter.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/lib-src/seccomp-filter.c b/lib-src/seccomp-filter.c new file mode 100644 index 00000000000..9918fb025ef --- /dev/null +++ b/lib-src/seccomp-filter.c | |||
| @@ -0,0 +1,321 @@ | |||
| 1 | /* Generate a Secure Computing filter definition file. | ||
| 2 | |||
| 3 | Copyright (C) 2020 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify it | ||
| 8 | under the terms of the GNU General Public License as published by the | ||
| 9 | Free Software Foundation, either version 3 of the License, or (at your | ||
| 10 | option) any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, but | ||
| 13 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along with GNU Emacs. If not, see | ||
| 19 | <https://www.gnu.org/licenses/>. */ | ||
| 20 | |||
| 21 | /* This program creates a small Secure Computing filter usable for a | ||
| 22 | typical minimal Emacs sandbox. See the man page for `seccomp' for | ||
| 23 | details about Secure Computing filters. This program requires the | ||
| 24 | `libseccomp' library. However, the resulting filter file requires | ||
| 25 | only a Linux kernel supporting the Secure Computing extension. | ||
| 26 | |||
| 27 | Usage: | ||
| 28 | |||
| 29 | seccomp-filter out.bpf out.pfc | ||
| 30 | |||
| 31 | This writes the raw `struct sock_filter' array to out.bpf and a | ||
| 32 | human-readable representation to out.pfc. */ | ||
| 33 | |||
| 34 | #include "config.h" | ||
| 35 | |||
| 36 | #include <errno.h> | ||
| 37 | #include <limits.h> | ||
| 38 | #include <stdarg.h> | ||
| 39 | #include <stdbool.h> | ||
| 40 | #include <stdlib.h> | ||
| 41 | #include <stdint.h> | ||
| 42 | #include <stdio.h> | ||
| 43 | |||
| 44 | #include <sys/ioctl.h> | ||
| 45 | #include <sys/mman.h> | ||
| 46 | #include <sys/prctl.h> | ||
| 47 | #include <sys/types.h> | ||
| 48 | #include <sys/stat.h> | ||
| 49 | #include <linux/futex.h> | ||
| 50 | #include <fcntl.h> | ||
| 51 | #include <sched.h> | ||
| 52 | #include <seccomp.h> | ||
| 53 | #include <unistd.h> | ||
| 54 | |||
| 55 | #include "verify.h" | ||
| 56 | |||
| 57 | static ATTRIBUTE_FORMAT_PRINTF (2, 3) _Noreturn void | ||
| 58 | fail (int error, const char *format, ...) | ||
| 59 | { | ||
| 60 | va_list ap; | ||
| 61 | va_start (ap, format); | ||
| 62 | if (error == 0) | ||
| 63 | vfprintf (stderr, format, ap); | ||
| 64 | else | ||
| 65 | { | ||
| 66 | char buffer[1000]; | ||
| 67 | vsnprintf (buffer, sizeof buffer, format, ap); | ||
| 68 | errno = error; | ||
| 69 | perror (buffer); | ||
| 70 | } | ||
| 71 | va_end (ap); | ||
| 72 | fflush (NULL); | ||
| 73 | exit (EXIT_FAILURE); | ||
| 74 | } | ||
| 75 | |||
| 76 | /* This binary is trivial, so we use a single global filter context | ||
| 77 | object that we release using `atexit'. */ | ||
| 78 | |||
| 79 | static scmp_filter_ctx ctx; | ||
| 80 | |||
| 81 | static void | ||
| 82 | release_context (void) | ||
| 83 | { | ||
| 84 | seccomp_release (ctx); | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Wrapper functions and macros for libseccomp functions. We exit | ||
| 88 | immediately upon any error to avoid error checking noise. */ | ||
| 89 | |||
| 90 | static void | ||
| 91 | set_attribute (enum scmp_filter_attr attr, uint32_t value) | ||
| 92 | { | ||
| 93 | int status = seccomp_attr_set (ctx, attr, value); | ||
| 94 | if (status < 0) | ||
| 95 | fail (-status, "seccomp_attr_set (ctx, %u, %u)", attr, value); | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Like `seccomp_rule_add (ACTION, SYSCALL, ...)', except that you | ||
| 99 | don't have to specify the number of comparator arguments, and any | ||
| 100 | failure will exit the process. */ | ||
| 101 | |||
| 102 | #define RULE(action, syscall, ...) \ | ||
| 103 | do \ | ||
| 104 | { \ | ||
| 105 | const struct scmp_arg_cmp arg_array[] = {__VA_ARGS__}; \ | ||
| 106 | enum { arg_cnt = sizeof arg_array / sizeof *arg_array }; \ | ||
| 107 | int status = seccomp_rule_add_array (ctx, (action), (syscall), \ | ||
| 108 | arg_cnt, arg_array); \ | ||
| 109 | if (status < 0) \ | ||
| 110 | fail (-status, "seccomp_rule_add_array (%s, %s, %d, {%s})", \ | ||
| 111 | #action, #syscall, arg_cnt, #__VA_ARGS__); \ | ||
| 112 | } \ | ||
| 113 | while (false) | ||
| 114 | |||
| 115 | static void | ||
| 116 | export_filter (const char *file, | ||
| 117 | int (*function) (const scmp_filter_ctx, int), | ||
| 118 | const char *name) | ||
| 119 | { | ||
| 120 | int fd = TEMP_FAILURE_RETRY ( | ||
| 121 | open (file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC, | ||
| 122 | 0644)); | ||
| 123 | if (fd < 0) | ||
| 124 | fail (errno, "open %s", file); | ||
| 125 | int status = function (ctx, fd); | ||
| 126 | if (status < 0) | ||
| 127 | fail (-status, "%s", name); | ||
| 128 | if (close (fd) != 0) | ||
| 129 | fail (errno, "close"); | ||
| 130 | } | ||
| 131 | |||
| 132 | #define EXPORT_FILTER(file, function) \ | ||
| 133 | export_filter ((file), (function), #function) | ||
| 134 | |||
| 135 | int | ||
| 136 | main (int argc, char **argv) | ||
| 137 | { | ||
| 138 | if (argc != 3) | ||
| 139 | fail (0, "usage: %s out.bpf out.pfc", argv[0]); | ||
| 140 | |||
| 141 | /* Any unhandled syscall should abort the Emacs process. */ | ||
| 142 | ctx = seccomp_init (SCMP_ACT_KILL_PROCESS); | ||
| 143 | if (ctx == NULL) | ||
| 144 | fail (0, "seccomp_init"); | ||
| 145 | atexit (release_context); | ||
| 146 | |||
| 147 | /* We want to abort immediately if the architecture is unknown. */ | ||
| 148 | set_attribute (SCMP_FLTATR_ACT_BADARCH, SCMP_ACT_KILL_PROCESS); | ||
| 149 | set_attribute (SCMP_FLTATR_CTL_NNP, 1); | ||
| 150 | set_attribute (SCMP_FLTATR_CTL_TSYNC, 1); | ||
| 151 | set_attribute (SCMP_FLTATR_CTL_LOG, 0); | ||
| 152 | |||
| 153 | verify (CHAR_BIT == 8); | ||
| 154 | verify (sizeof (int) == 4 && INT_MIN == INT32_MIN | ||
| 155 | && INT_MAX == INT32_MAX); | ||
| 156 | verify (sizeof (void *) == 8); | ||
| 157 | verify ((uintptr_t) NULL == 0); | ||
| 158 | |||
| 159 | /* Allow a clean exit. */ | ||
| 160 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (exit)); | ||
| 161 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (exit_group)); | ||
| 162 | |||
| 163 | /* Allow `mmap' and friends. This is necessary for dynamic loading, | ||
| 164 | reading the portable dump file, and thread creation. We don't | ||
| 165 | allow pages to be both writable and executable. */ | ||
| 166 | verify (MAP_PRIVATE != 0); | ||
| 167 | verify (MAP_SHARED != 0); | ||
| 168 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (mmap), | ||
| 169 | SCMP_A2_32 (SCMP_CMP_MASKED_EQ, | ||
| 170 | ~(PROT_NONE | PROT_READ | PROT_WRITE)), | ||
| 171 | /* Only support known flags. MAP_DENYWRITE is ignored, but | ||
| 172 | some versions of the dynamic loader still use it. Also | ||
| 173 | allow allocating thread stacks. */ | ||
| 174 | SCMP_A3_32 (SCMP_CMP_MASKED_EQ, | ||
| 175 | ~(MAP_PRIVATE | MAP_FILE | MAP_ANONYMOUS | ||
| 176 | | MAP_FIXED | MAP_DENYWRITE | MAP_STACK | ||
| 177 | | MAP_NORESERVE), | ||
| 178 | 0)); | ||
| 179 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (mmap), | ||
| 180 | SCMP_A2_32 (SCMP_CMP_MASKED_EQ, | ||
| 181 | ~(PROT_NONE | PROT_READ | PROT_EXEC)), | ||
| 182 | /* Only support known flags. MAP_DENYWRITE is ignored, but | ||
| 183 | some versions of the dynamic loader still use it. */ | ||
| 184 | SCMP_A3_32 (SCMP_CMP_MASKED_EQ, | ||
| 185 | ~(MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | ||
| 186 | | MAP_DENYWRITE), | ||
| 187 | 0)); | ||
| 188 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (munmap)); | ||
| 189 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (mprotect), | ||
| 190 | /* Don't allow making pages executable. */ | ||
| 191 | SCMP_A2_32 (SCMP_CMP_MASKED_EQ, | ||
| 192 | ~(PROT_NONE | PROT_READ | PROT_WRITE), 0)); | ||
| 193 | |||
| 194 | /* Futexes are used everywhere. */ | ||
| 195 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (futex), | ||
| 196 | SCMP_A1_32 (SCMP_CMP_EQ, FUTEX_WAKE_PRIVATE)); | ||
| 197 | |||
| 198 | /* Allow basic dynamic memory management. */ | ||
| 199 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (brk)); | ||
| 200 | |||
| 201 | /* Allow some status inquiries. */ | ||
| 202 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (uname)); | ||
| 203 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (getuid)); | ||
| 204 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (geteuid)); | ||
| 205 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (getpid)); | ||
| 206 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (getpgrp)); | ||
| 207 | |||
| 208 | /* Allow operations on open file descriptors. File descriptors are | ||
| 209 | capabilities, and operating on them shouldn't cause security | ||
| 210 | issues. */ | ||
| 211 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (read)); | ||
| 212 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (write)); | ||
| 213 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (close)); | ||
| 214 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (lseek)); | ||
| 215 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (dup)); | ||
| 216 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (dup2)); | ||
| 217 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (fstat)); | ||
| 218 | |||
| 219 | /* Allow read operations on the filesystem. If necessary, these | ||
| 220 | should be further restricted using mount namespaces. */ | ||
| 221 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (access)); | ||
| 222 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (faccessat)); | ||
| 223 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (stat)); | ||
| 224 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (stat64)); | ||
| 225 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (lstat)); | ||
| 226 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (lstat64)); | ||
| 227 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (fstatat64)); | ||
| 228 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (newfstatat)); | ||
| 229 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (readlink)); | ||
| 230 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (readlinkat)); | ||
| 231 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (getcwd)); | ||
| 232 | |||
| 233 | /* Allow opening files, assuming they are only opened for | ||
| 234 | reading. */ | ||
| 235 | verify (O_WRONLY != 0); | ||
| 236 | verify (O_RDWR != 0); | ||
| 237 | verify (O_CREAT != 0); | ||
| 238 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (open), | ||
| 239 | SCMP_A1_32 (SCMP_CMP_MASKED_EQ, | ||
| 240 | ~(O_RDONLY | O_BINARY | O_CLOEXEC | O_PATH | ||
| 241 | | O_DIRECTORY), | ||
| 242 | 0)); | ||
| 243 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (openat), | ||
| 244 | SCMP_A2_32 (SCMP_CMP_MASKED_EQ, | ||
| 245 | ~(O_RDONLY | O_BINARY | O_CLOEXEC | O_PATH | ||
| 246 | | O_DIRECTORY), | ||
| 247 | 0)); | ||
| 248 | |||
| 249 | /* Allow `tcgetpgrp'. */ | ||
| 250 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (ioctl), | ||
| 251 | SCMP_A0_32 (SCMP_CMP_EQ, STDIN_FILENO), | ||
| 252 | SCMP_A1_32 (SCMP_CMP_EQ, TIOCGPGRP)); | ||
| 253 | |||
| 254 | /* Allow reading (but not setting) file flags. */ | ||
| 255 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (fcntl), | ||
| 256 | SCMP_A1_32 (SCMP_CMP_EQ, F_GETFL)); | ||
| 257 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (fcntl64), | ||
| 258 | SCMP_A1_32 (SCMP_CMP_EQ, F_GETFL)); | ||
| 259 | |||
| 260 | /* Allow reading random numbers from the kernel. */ | ||
| 261 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (getrandom)); | ||
| 262 | |||
| 263 | /* Changing the umask is uncritical. */ | ||
| 264 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (umask)); | ||
| 265 | |||
| 266 | /* Allow creation of pipes. */ | ||
| 267 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (pipe)); | ||
| 268 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (pipe2)); | ||
| 269 | |||
| 270 | /* Allow reading (but not changing) resource limits. */ | ||
| 271 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (getrlimit)); | ||
| 272 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (prlimit64), | ||
| 273 | SCMP_A0_32 (SCMP_CMP_EQ, 0) /* pid == 0 (current process) */, | ||
| 274 | SCMP_A2_64 (SCMP_CMP_EQ, 0) /* new_limit == NULL */); | ||
| 275 | |||
| 276 | /* Block changing resource limits, but don't crash. */ | ||
| 277 | RULE (SCMP_ACT_ERRNO (EPERM), SCMP_SYS (prlimit64), | ||
| 278 | SCMP_A0_32 (SCMP_CMP_EQ, 0) /* pid == 0 (current process) */, | ||
| 279 | SCMP_A2_64 (SCMP_CMP_NE, 0) /* new_limit != NULL */); | ||
| 280 | |||
| 281 | /* Emacs installs signal handlers, which is harmless. */ | ||
| 282 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (sigaction)); | ||
| 283 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (rt_sigaction)); | ||
| 284 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (sigprocmask)); | ||
| 285 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (rt_sigprocmask)); | ||
| 286 | |||
| 287 | /* Allow timer support. */ | ||
| 288 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (timer_create)); | ||
| 289 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (timerfd_create)); | ||
| 290 | |||
| 291 | /* Allow thread creation. See the NOTES section in the manual page | ||
| 292 | for the `clone' function. */ | ||
| 293 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (clone), | ||
| 294 | SCMP_A0_64 (SCMP_CMP_MASKED_EQ, | ||
| 295 | /* Flags needed to create threads. See | ||
| 296 | create_thread in libc. */ | ||
| 297 | ~(CLONE_VM | CLONE_FS | CLONE_FILES | ||
| 298 | | CLONE_SYSVSEM | CLONE_SIGHAND | CLONE_THREAD | ||
| 299 | | CLONE_SETTLS | CLONE_PARENT_SETTID | ||
| 300 | | CLONE_CHILD_CLEARTID), | ||
| 301 | 0)); | ||
| 302 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (sigaltstack)); | ||
| 303 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (set_robust_list)); | ||
| 304 | |||
| 305 | /* Allow setting the process name for new threads. */ | ||
| 306 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (prctl), | ||
| 307 | SCMP_A0_32 (SCMP_CMP_EQ, PR_SET_NAME)); | ||
| 308 | |||
| 309 | /* Allow some event handling functions used by glib. */ | ||
| 310 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (eventfd)); | ||
| 311 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (eventfd2)); | ||
| 312 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (wait4)); | ||
| 313 | RULE (SCMP_ACT_ALLOW, SCMP_SYS (poll)); | ||
| 314 | |||
| 315 | /* Don't allow creating sockets (network access would be extremely | ||
| 316 | dangerous), but also don't crash. */ | ||
| 317 | RULE (SCMP_ACT_ERRNO (EACCES), SCMP_SYS (socket)); | ||
| 318 | |||
| 319 | EXPORT_FILTER (argv[1], seccomp_export_bpf); | ||
| 320 | EXPORT_FILTER (argv[2], seccomp_export_pfc); | ||
| 321 | } | ||