1239844Sdes/* 2239844Sdes * Copyright (c) 2012 Will Drewry <wad@dataspill.org> 3239844Sdes * 4239844Sdes * Permission to use, copy, modify, and distribute this software for any 5239844Sdes * purpose with or without fee is hereby granted, provided that the above 6239844Sdes * copyright notice and this permission notice appear in all copies. 7239844Sdes * 8239844Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9239844Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10239844Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11239844Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12239844Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13239844Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14239844Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15239844Sdes */ 16239844Sdes 17239844Sdes/* 18239844Sdes * Uncomment the SANDBOX_SECCOMP_FILTER_DEBUG macro below to help diagnose 19239844Sdes * filter breakage during development. *Do not* use this in production, 20239844Sdes * as it relies on making library calls that are unsafe in signal context. 21239844Sdes * 22239844Sdes * Instead, live systems the auditctl(8) may be used to monitor failures. 23239844Sdes * E.g. 24239844Sdes * auditctl -a task,always -F uid=<privsep uid> 25239844Sdes */ 26239844Sdes/* #define SANDBOX_SECCOMP_FILTER_DEBUG 1 */ 27239844Sdes 28239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 29239844Sdes/* Use the kernel headers in case of an older toolchain. */ 30239844Sdes# include <asm/siginfo.h> 31239844Sdes# define __have_siginfo_t 1 32239844Sdes# define __have_sigval_t 1 33239844Sdes# define __have_sigevent_t 1 34239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 35239844Sdes 36239844Sdes#include "includes.h" 37239844Sdes 38239844Sdes#ifdef SANDBOX_SECCOMP_FILTER 39239844Sdes 40239844Sdes#include <sys/types.h> 41239844Sdes#include <sys/resource.h> 42239844Sdes#include <sys/prctl.h> 43239844Sdes 44239844Sdes#include <linux/audit.h> 45239844Sdes#include <linux/filter.h> 46239844Sdes#include <linux/seccomp.h> 47248613Sdes#include <elf.h> 48239844Sdes 49239844Sdes#include <asm/unistd.h> 50239844Sdes 51239844Sdes#include <errno.h> 52239844Sdes#include <signal.h> 53239844Sdes#include <stdarg.h> 54239844Sdes#include <stddef.h> /* for offsetof */ 55239844Sdes#include <stdio.h> 56239844Sdes#include <stdlib.h> 57239844Sdes#include <string.h> 58239844Sdes#include <unistd.h> 59239844Sdes 60239844Sdes#include "log.h" 61239844Sdes#include "ssh-sandbox.h" 62239844Sdes#include "xmalloc.h" 63239844Sdes 64239844Sdes/* Linux seccomp_filter sandbox */ 65239844Sdes#define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL 66239844Sdes 67239844Sdes/* Use a signal handler to emit violations when debugging */ 68239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 69239844Sdes# undef SECCOMP_FILTER_FAIL 70239844Sdes# define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP 71239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 72239844Sdes 73239844Sdes/* Simple helpers to avoid manual errors (but larger BPF programs). */ 74239844Sdes#define SC_DENY(_nr, _errno) \ 75239844Sdes BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ 76239844Sdes BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno)) 77239844Sdes#define SC_ALLOW(_nr) \ 78239844Sdes BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ 79239844Sdes BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) 80239844Sdes 81239844Sdes/* Syscall filtering set for preauth. */ 82239844Sdesstatic const struct sock_filter preauth_insns[] = { 83239844Sdes /* Ensure the syscall arch convention is as expected. */ 84239844Sdes BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 85239844Sdes offsetof(struct seccomp_data, arch)), 86239844Sdes BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0), 87239844Sdes BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), 88239844Sdes /* Load the syscall number for checking. */ 89239844Sdes BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 90239844Sdes offsetof(struct seccomp_data, nr)), 91239844Sdes SC_DENY(open, EACCES), 92239844Sdes SC_ALLOW(getpid), 93239844Sdes SC_ALLOW(gettimeofday), 94255767Sdes SC_ALLOW(clock_gettime), 95248613Sdes#ifdef __NR_time /* not defined on EABI ARM */ 96239844Sdes SC_ALLOW(time), 97248613Sdes#endif 98239844Sdes SC_ALLOW(read), 99239844Sdes SC_ALLOW(write), 100239844Sdes SC_ALLOW(close), 101239844Sdes SC_ALLOW(brk), 102239844Sdes SC_ALLOW(poll), 103239844Sdes#ifdef __NR__newselect 104239844Sdes SC_ALLOW(_newselect), 105239844Sdes#else 106239844Sdes SC_ALLOW(select), 107239844Sdes#endif 108239844Sdes SC_ALLOW(madvise), 109248613Sdes#ifdef __NR_mmap2 /* EABI ARM only has mmap2() */ 110248613Sdes SC_ALLOW(mmap2), 111248613Sdes#endif 112248613Sdes#ifdef __NR_mmap 113239844Sdes SC_ALLOW(mmap), 114248613Sdes#endif 115239844Sdes SC_ALLOW(munmap), 116239844Sdes SC_ALLOW(exit_group), 117239844Sdes#ifdef __NR_rt_sigprocmask 118239844Sdes SC_ALLOW(rt_sigprocmask), 119239844Sdes#else 120239844Sdes SC_ALLOW(sigprocmask), 121239844Sdes#endif 122239844Sdes BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), 123239844Sdes}; 124239844Sdes 125239844Sdesstatic const struct sock_fprog preauth_program = { 126239844Sdes .len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])), 127239844Sdes .filter = (struct sock_filter *)preauth_insns, 128239844Sdes}; 129239844Sdes 130239844Sdesstruct ssh_sandbox { 131239844Sdes pid_t child_pid; 132239844Sdes}; 133239844Sdes 134239844Sdesstruct ssh_sandbox * 135239844Sdesssh_sandbox_init(void) 136239844Sdes{ 137239844Sdes struct ssh_sandbox *box; 138239844Sdes 139239844Sdes /* 140239844Sdes * Strictly, we don't need to maintain any state here but we need 141239844Sdes * to return non-NULL to satisfy the API. 142239844Sdes */ 143239844Sdes debug3("%s: preparing seccomp filter sandbox", __func__); 144239844Sdes box = xcalloc(1, sizeof(*box)); 145239844Sdes box->child_pid = 0; 146239844Sdes 147239844Sdes return box; 148239844Sdes} 149239844Sdes 150239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 151239844Sdesextern struct monitor *pmonitor; 152239844Sdesvoid mm_log_handler(LogLevel level, const char *msg, void *ctx); 153239844Sdes 154239844Sdesstatic void 155239844Sdesssh_sandbox_violation(int signum, siginfo_t *info, void *void_context) 156239844Sdes{ 157239844Sdes char msg[256]; 158239844Sdes 159239844Sdes snprintf(msg, sizeof(msg), 160239844Sdes "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)", 161239844Sdes __func__, info->si_arch, info->si_syscall, info->si_call_addr); 162239844Sdes mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor); 163239844Sdes _exit(1); 164239844Sdes} 165239844Sdes 166239844Sdesstatic void 167239844Sdesssh_sandbox_child_debugging(void) 168239844Sdes{ 169239844Sdes struct sigaction act; 170239844Sdes sigset_t mask; 171239844Sdes 172239844Sdes debug3("%s: installing SIGSYS handler", __func__); 173239844Sdes memset(&act, 0, sizeof(act)); 174239844Sdes sigemptyset(&mask); 175239844Sdes sigaddset(&mask, SIGSYS); 176239844Sdes 177239844Sdes act.sa_sigaction = &ssh_sandbox_violation; 178239844Sdes act.sa_flags = SA_SIGINFO; 179239844Sdes if (sigaction(SIGSYS, &act, NULL) == -1) 180239844Sdes fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno)); 181239844Sdes if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) 182239844Sdes fatal("%s: sigprocmask(SIGSYS): %s", 183239844Sdes __func__, strerror(errno)); 184239844Sdes} 185239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 186239844Sdes 187239844Sdesvoid 188239844Sdesssh_sandbox_child(struct ssh_sandbox *box) 189239844Sdes{ 190239844Sdes struct rlimit rl_zero; 191239849Sdes int nnp_failed = 0; 192239844Sdes 193239844Sdes /* Set rlimits for completeness if possible. */ 194239844Sdes rl_zero.rlim_cur = rl_zero.rlim_max = 0; 195239844Sdes if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) 196239844Sdes fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", 197239844Sdes __func__, strerror(errno)); 198239844Sdes if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) 199239844Sdes fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", 200239844Sdes __func__, strerror(errno)); 201239844Sdes if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) 202239844Sdes fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", 203239844Sdes __func__, strerror(errno)); 204239844Sdes 205239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 206239844Sdes ssh_sandbox_child_debugging(); 207239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 208239844Sdes 209239844Sdes debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__); 210239849Sdes if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { 211239849Sdes debug("%s: prctl(PR_SET_NO_NEW_PRIVS): %s", 212239844Sdes __func__, strerror(errno)); 213239849Sdes nnp_failed = 1; 214239849Sdes } 215239844Sdes debug3("%s: attaching seccomp filter program", __func__); 216239844Sdes if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1) 217239849Sdes debug("%s: prctl(PR_SET_SECCOMP): %s", 218239844Sdes __func__, strerror(errno)); 219239849Sdes else if (nnp_failed) 220239849Sdes fatal("%s: SECCOMP_MODE_FILTER activated but " 221239849Sdes "PR_SET_NO_NEW_PRIVS failed", __func__); 222239844Sdes} 223239844Sdes 224239844Sdesvoid 225239844Sdesssh_sandbox_parent_finish(struct ssh_sandbox *box) 226239844Sdes{ 227239844Sdes free(box); 228239844Sdes debug3("%s: finished", __func__); 229239844Sdes} 230239844Sdes 231239844Sdesvoid 232239844Sdesssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) 233239844Sdes{ 234239844Sdes box->child_pid = child_pid; 235239844Sdes} 236239844Sdes 237239844Sdes#endif /* SANDBOX_SECCOMP_FILTER */ 238