sandbox-seccomp-filter.c revision 239846
1/* 2 * Copyright (c) 2012 Will Drewry <wad@dataspill.org> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17/* 18 * Uncomment the SANDBOX_SECCOMP_FILTER_DEBUG macro below to help diagnose 19 * filter breakage during development. *Do not* use this in production, 20 * as it relies on making library calls that are unsafe in signal context. 21 * 22 * Instead, live systems the auditctl(8) may be used to monitor failures. 23 * E.g. 24 * auditctl -a task,always -F uid=<privsep uid> 25 */ 26/* #define SANDBOX_SECCOMP_FILTER_DEBUG 1 */ 27 28#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 29/* Use the kernel headers in case of an older toolchain. */ 30# include <asm/siginfo.h> 31# define __have_siginfo_t 1 32# define __have_sigval_t 1 33# define __have_sigevent_t 1 34#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 35 36#include "includes.h" 37 38#ifdef SANDBOX_SECCOMP_FILTER 39 40#include <sys/types.h> 41#include <sys/resource.h> 42#include <sys/prctl.h> 43 44#include <linux/audit.h> 45#include <linux/filter.h> 46#include <linux/seccomp.h> 47 48#include <asm/unistd.h> 49 50#include <errno.h> 51#include <signal.h> 52#include <stdarg.h> 53#include <stddef.h> /* for offsetof */ 54#include <stdio.h> 55#include <stdlib.h> 56#include <string.h> 57#include <unistd.h> 58 59#include "log.h" 60#include "ssh-sandbox.h" 61#include "xmalloc.h" 62 63/* Linux seccomp_filter sandbox */ 64#define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL 65 66/* Use a signal handler to emit violations when debugging */ 67#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 68# undef SECCOMP_FILTER_FAIL 69# define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP 70#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 71 72/* Simple helpers to avoid manual errors (but larger BPF programs). */ 73#define SC_DENY(_nr, _errno) \ 74 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ 75 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno)) 76#define SC_ALLOW(_nr) \ 77 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ 78 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) 79 80/* Syscall filtering set for preauth. */ 81static const struct sock_filter preauth_insns[] = { 82 /* Ensure the syscall arch convention is as expected. */ 83 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 84 offsetof(struct seccomp_data, arch)), 85 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0), 86 BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), 87 /* Load the syscall number for checking. */ 88 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 89 offsetof(struct seccomp_data, nr)), 90 SC_DENY(open, EACCES), 91 SC_ALLOW(getpid), 92 SC_ALLOW(gettimeofday), 93 SC_ALLOW(time), 94 SC_ALLOW(read), 95 SC_ALLOW(write), 96 SC_ALLOW(close), 97 SC_ALLOW(brk), 98 SC_ALLOW(poll), 99#ifdef __NR__newselect 100 SC_ALLOW(_newselect), 101#else 102 SC_ALLOW(select), 103#endif 104 SC_ALLOW(madvise), 105 SC_ALLOW(mmap), 106 SC_ALLOW(munmap), 107 SC_ALLOW(exit_group), 108#ifdef __NR_rt_sigprocmask 109 SC_ALLOW(rt_sigprocmask), 110#else 111 SC_ALLOW(sigprocmask), 112#endif 113 BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), 114}; 115 116static const struct sock_fprog preauth_program = { 117 .len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])), 118 .filter = (struct sock_filter *)preauth_insns, 119}; 120 121struct ssh_sandbox { 122 pid_t child_pid; 123}; 124 125struct ssh_sandbox * 126ssh_sandbox_init(void) 127{ 128 struct ssh_sandbox *box; 129 130 /* 131 * Strictly, we don't need to maintain any state here but we need 132 * to return non-NULL to satisfy the API. 133 */ 134 debug3("%s: preparing seccomp filter sandbox", __func__); 135 box = xcalloc(1, sizeof(*box)); 136 box->child_pid = 0; 137 138 return box; 139} 140 141#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 142extern struct monitor *pmonitor; 143void mm_log_handler(LogLevel level, const char *msg, void *ctx); 144 145static void 146ssh_sandbox_violation(int signum, siginfo_t *info, void *void_context) 147{ 148 char msg[256]; 149 150 snprintf(msg, sizeof(msg), 151 "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)", 152 __func__, info->si_arch, info->si_syscall, info->si_call_addr); 153 mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor); 154 _exit(1); 155} 156 157static void 158ssh_sandbox_child_debugging(void) 159{ 160 struct sigaction act; 161 sigset_t mask; 162 163 debug3("%s: installing SIGSYS handler", __func__); 164 memset(&act, 0, sizeof(act)); 165 sigemptyset(&mask); 166 sigaddset(&mask, SIGSYS); 167 168 act.sa_sigaction = &ssh_sandbox_violation; 169 act.sa_flags = SA_SIGINFO; 170 if (sigaction(SIGSYS, &act, NULL) == -1) 171 fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno)); 172 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) 173 fatal("%s: sigprocmask(SIGSYS): %s", 174 __func__, strerror(errno)); 175} 176#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 177 178void 179ssh_sandbox_child(struct ssh_sandbox *box) 180{ 181 struct rlimit rl_zero; 182 183 /* Set rlimits for completeness if possible. */ 184 rl_zero.rlim_cur = rl_zero.rlim_max = 0; 185 if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) 186 fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", 187 __func__, strerror(errno)); 188 if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) 189 fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", 190 __func__, strerror(errno)); 191 if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) 192 fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", 193 __func__, strerror(errno)); 194 195#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 196 ssh_sandbox_child_debugging(); 197#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 198 199 debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__); 200 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) 201 fatal("%s: prctl(PR_SET_NO_NEW_PRIVS): %s", 202 __func__, strerror(errno)); 203 debug3("%s: attaching seccomp filter program", __func__); 204 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1) 205 fatal("%s: prctl(PR_SET_SECCOMP): %s", 206 __func__, strerror(errno)); 207} 208 209void 210ssh_sandbox_parent_finish(struct ssh_sandbox *box) 211{ 212 free(box); 213 debug3("%s: finished", __func__); 214} 215 216void 217ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) 218{ 219 box->child_pid = child_pid; 220} 221 222#endif /* SANDBOX_SECCOMP_FILTER */ 223