Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | // SPDX-License-Identifier: GPL-2.0 /* * Seccomp filter example for x86 (32-bit and 64-bit) with BPF macros * * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org> * Author: Will Drewry <wad@chromium.org> * * The code may be used by anyone for any purpose, * and can serve as a starting point for developing * applications using prctl(PR_SET_SECCOMP, 2, ...). */ #if defined(__i386__) || defined(__x86_64__) #define SUPPORTED_ARCH 1 #endif #if defined(SUPPORTED_ARCH) #define __USE_GNU 1 #define _GNU_SOURCE 1 #include <linux/types.h> #include <linux/filter.h> #include <linux/seccomp.h> #include <linux/unistd.h> #include <signal.h> #include <stdio.h> #include <stddef.h> #include <string.h> #include <sys/prctl.h> #include <unistd.h> #define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n])) #define syscall_nr (offsetof(struct seccomp_data, nr)) #if defined(__i386__) #define REG_RESULT REG_EAX #define REG_SYSCALL REG_EAX #define REG_ARG0 REG_EBX #define REG_ARG1 REG_ECX #define REG_ARG2 REG_EDX #define REG_ARG3 REG_ESI #define REG_ARG4 REG_EDI #define REG_ARG5 REG_EBP #elif defined(__x86_64__) #define REG_RESULT REG_RAX #define REG_SYSCALL REG_RAX #define REG_ARG0 REG_RDI #define REG_ARG1 REG_RSI #define REG_ARG2 REG_RDX #define REG_ARG3 REG_R10 #define REG_ARG4 REG_R8 #define REG_ARG5 REG_R9 #endif #ifndef PR_SET_NO_NEW_PRIVS #define PR_SET_NO_NEW_PRIVS 38 #endif #ifndef SYS_SECCOMP #define SYS_SECCOMP 1 #endif static void emulator(int nr, siginfo_t *info, void *void_context) { ucontext_t *ctx = (ucontext_t *)(void_context); int syscall; char *buf; ssize_t bytes; size_t len; if (info->si_code != SYS_SECCOMP) return; if (!ctx) return; syscall = ctx->uc_mcontext.gregs[REG_SYSCALL]; buf = (char *) ctx->uc_mcontext.gregs[REG_ARG1]; len = (size_t) ctx->uc_mcontext.gregs[REG_ARG2]; if (syscall != __NR_write) return; if (ctx->uc_mcontext.gregs[REG_ARG0] != STDERR_FILENO) return; /* Redirect stderr messages to stdout. Doesn't handle EINTR, etc */ ctx->uc_mcontext.gregs[REG_RESULT] = -1; if (write(STDOUT_FILENO, "[ERR] ", 6) > 0) { bytes = write(STDOUT_FILENO, buf, len); ctx->uc_mcontext.gregs[REG_RESULT] = bytes; } return; } static int install_emulator(void) { struct sigaction act; sigset_t mask; memset(&act, 0, sizeof(act)); sigemptyset(&mask); sigaddset(&mask, SIGSYS); act.sa_sigaction = &emulator; act.sa_flags = SA_SIGINFO; if (sigaction(SIGSYS, &act, NULL) < 0) { perror("sigaction"); return -1; } if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) { perror("sigprocmask"); return -1; } return 0; } static int install_filter(void) { struct sock_filter filter[] = { /* Grab the system call number */ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr), /* Jump table for the allowed syscalls */ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 0, 1), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), #ifdef __NR_sigreturn BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 0, 1), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), #endif BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit_group, 0, 1), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 0, 1), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_read, 1, 0), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 3, 2), /* Check that read is only using stdin. */ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_arg(0)), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDIN_FILENO, 4, 0), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL), /* Check that write is only using stdout */ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_arg(0)), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDOUT_FILENO, 1, 0), /* Trap attempts to write to stderr */ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDERR_FILENO, 1, 2), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL), }; struct sock_fprog prog = { .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), .filter = filter, }; if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("prctl(NO_NEW_PRIVS)"); return 1; } if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { perror("prctl"); return 1; } return 0; } #define payload(_c) (_c), sizeof((_c)) int main(int argc, char **argv) { char buf[4096]; ssize_t bytes = 0; if (install_emulator()) return 1; if (install_filter()) return 1; syscall(__NR_write, STDOUT_FILENO, payload("OHAI! WHAT IS YOUR NAME? ")); bytes = syscall(__NR_read, STDIN_FILENO, buf, sizeof(buf)); syscall(__NR_write, STDOUT_FILENO, payload("HELLO, ")); syscall(__NR_write, STDOUT_FILENO, buf, bytes); syscall(__NR_write, STDERR_FILENO, payload("Error message going to STDERR\n")); return 0; } #else /* SUPPORTED_ARCH */ /* * This sample is x86-only. Since kernel samples are compiled with the * host toolchain, a non-x86 host will result in using only the main() * below. */ int main(void) { return 1; } #endif /* SUPPORTED_ARCH */ |