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), 94263970Sdes 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), 101263970Sdes#ifdef __NR_shutdown /* not defined on archs that go via socketcall(2) */ 102263970Sdes SC_ALLOW(shutdown), 103263970Sdes#endif 104239844Sdes SC_ALLOW(brk), 105239844Sdes SC_ALLOW(poll), 106239844Sdes#ifdef __NR__newselect 107239844Sdes SC_ALLOW(_newselect), 108239844Sdes#else 109239844Sdes SC_ALLOW(select), 110239844Sdes#endif 111239844Sdes SC_ALLOW(madvise), 112248613Sdes#ifdef __NR_mmap2 /* EABI ARM only has mmap2() */ 113248613Sdes SC_ALLOW(mmap2), 114248613Sdes#endif 115248613Sdes#ifdef __NR_mmap 116239844Sdes SC_ALLOW(mmap), 117248613Sdes#endif 118239844Sdes SC_ALLOW(munmap), 119239844Sdes SC_ALLOW(exit_group), 120239844Sdes#ifdef __NR_rt_sigprocmask 121239844Sdes SC_ALLOW(rt_sigprocmask), 122239844Sdes#else 123239844Sdes SC_ALLOW(sigprocmask), 124239844Sdes#endif 125239844Sdes BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), 126239844Sdes}; 127239844Sdes 128239844Sdesstatic const struct sock_fprog preauth_program = { 129239844Sdes .len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])), 130239844Sdes .filter = (struct sock_filter *)preauth_insns, 131239844Sdes}; 132239844Sdes 133239844Sdesstruct ssh_sandbox { 134239844Sdes pid_t child_pid; 135239844Sdes}; 136239844Sdes 137239844Sdesstruct ssh_sandbox * 138263970Sdesssh_sandbox_init(struct monitor *monitor) 139239844Sdes{ 140239844Sdes struct ssh_sandbox *box; 141239844Sdes 142239844Sdes /* 143239844Sdes * Strictly, we don't need to maintain any state here but we need 144239844Sdes * to return non-NULL to satisfy the API. 145239844Sdes */ 146239844Sdes debug3("%s: preparing seccomp filter sandbox", __func__); 147239844Sdes box = xcalloc(1, sizeof(*box)); 148239844Sdes box->child_pid = 0; 149239844Sdes 150239844Sdes return box; 151239844Sdes} 152239844Sdes 153239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 154239844Sdesextern struct monitor *pmonitor; 155239844Sdesvoid mm_log_handler(LogLevel level, const char *msg, void *ctx); 156239844Sdes 157239844Sdesstatic void 158239844Sdesssh_sandbox_violation(int signum, siginfo_t *info, void *void_context) 159239844Sdes{ 160239844Sdes char msg[256]; 161239844Sdes 162239844Sdes snprintf(msg, sizeof(msg), 163239844Sdes "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)", 164239844Sdes __func__, info->si_arch, info->si_syscall, info->si_call_addr); 165239844Sdes mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor); 166239844Sdes _exit(1); 167239844Sdes} 168239844Sdes 169239844Sdesstatic void 170239844Sdesssh_sandbox_child_debugging(void) 171239844Sdes{ 172239844Sdes struct sigaction act; 173239844Sdes sigset_t mask; 174239844Sdes 175239844Sdes debug3("%s: installing SIGSYS handler", __func__); 176239844Sdes memset(&act, 0, sizeof(act)); 177239844Sdes sigemptyset(&mask); 178239844Sdes sigaddset(&mask, SIGSYS); 179239844Sdes 180239844Sdes act.sa_sigaction = &ssh_sandbox_violation; 181239844Sdes act.sa_flags = SA_SIGINFO; 182239844Sdes if (sigaction(SIGSYS, &act, NULL) == -1) 183239844Sdes fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno)); 184239844Sdes if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) 185239844Sdes fatal("%s: sigprocmask(SIGSYS): %s", 186239844Sdes __func__, strerror(errno)); 187239844Sdes} 188239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 189239844Sdes 190239844Sdesvoid 191239844Sdesssh_sandbox_child(struct ssh_sandbox *box) 192239844Sdes{ 193239844Sdes struct rlimit rl_zero; 194239849Sdes int nnp_failed = 0; 195239844Sdes 196239844Sdes /* Set rlimits for completeness if possible. */ 197239844Sdes rl_zero.rlim_cur = rl_zero.rlim_max = 0; 198239844Sdes if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) 199239844Sdes fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", 200239844Sdes __func__, strerror(errno)); 201239844Sdes if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) 202239844Sdes fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", 203239844Sdes __func__, strerror(errno)); 204239844Sdes if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) 205239844Sdes fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", 206239844Sdes __func__, strerror(errno)); 207239844Sdes 208239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG 209239844Sdes ssh_sandbox_child_debugging(); 210239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 211239844Sdes 212239844Sdes debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__); 213239849Sdes if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { 214239849Sdes debug("%s: prctl(PR_SET_NO_NEW_PRIVS): %s", 215239844Sdes __func__, strerror(errno)); 216239849Sdes nnp_failed = 1; 217239849Sdes } 218239844Sdes debug3("%s: attaching seccomp filter program", __func__); 219239844Sdes if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1) 220239849Sdes debug("%s: prctl(PR_SET_SECCOMP): %s", 221239844Sdes __func__, strerror(errno)); 222239849Sdes else if (nnp_failed) 223239849Sdes fatal("%s: SECCOMP_MODE_FILTER activated but " 224239849Sdes "PR_SET_NO_NEW_PRIVS failed", __func__); 225239844Sdes} 226239844Sdes 227239844Sdesvoid 228239844Sdesssh_sandbox_parent_finish(struct ssh_sandbox *box) 229239844Sdes{ 230239844Sdes free(box); 231239844Sdes debug3("%s: finished", __func__); 232239844Sdes} 233239844Sdes 234239844Sdesvoid 235239844Sdesssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) 236239844Sdes{ 237239844Sdes box->child_pid = child_pid; 238239844Sdes} 239239844Sdes 240239844Sdes#endif /* SANDBOX_SECCOMP_FILTER */ 241