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 #include <elf.h> 48 49 #include <asm/unistd.h> 50 51 #include <errno.h> 52 #include <signal.h> 53 #include <stdarg.h> 54 #include <stddef.h> /* for offsetof */ 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #include "log.h" 61 #include "ssh-sandbox.h" 62 #include "xmalloc.h" 63 64 /* Linux seccomp_filter sandbox */ 65 #define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL 66 67 /* Use a signal handler to emit violations when debugging */ 68 #ifdef SANDBOX_SECCOMP_FILTER_DEBUG 69 # undef SECCOMP_FILTER_FAIL 70 # define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP 71 #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 72 73 /* Simple helpers to avoid manual errors (but larger BPF programs). */ 74 #define SC_DENY(_nr, _errno) \ 75 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ 76 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno)) 77 #define SC_ALLOW(_nr) \ 78 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ 79 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) 80 81 /* Syscall filtering set for preauth. */ 82 static const struct sock_filter preauth_insns[] = { 83 /* Ensure the syscall arch convention is as expected. */ 84 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 85 offsetof(struct seccomp_data, arch)), 86 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0), 87 BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), 88 /* Load the syscall number for checking. */ 89 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 90 offsetof(struct seccomp_data, nr)), 91 SC_DENY(open, EACCES), 92 SC_ALLOW(getpid), 93 SC_ALLOW(gettimeofday), 94 SC_ALLOW(clock_gettime), 95 #ifdef __NR_time /* not defined on EABI ARM */ 96 SC_ALLOW(time), 97 #endif 98 SC_ALLOW(read), 99 SC_ALLOW(write), 100 SC_ALLOW(close), 101 #ifdef __NR_shutdown /* not defined on archs that go via socketcall(2) */ 102 SC_ALLOW(shutdown), 103 #endif 104 SC_ALLOW(brk), 105 SC_ALLOW(poll), 106 #ifdef __NR__newselect 107 SC_ALLOW(_newselect), 108 #else 109 SC_ALLOW(select), 110 #endif 111 SC_ALLOW(madvise), 112 #ifdef __NR_mmap2 /* EABI ARM only has mmap2() */ 113 SC_ALLOW(mmap2), 114 #endif 115 #ifdef __NR_mmap 116 SC_ALLOW(mmap), 117 #endif 118 SC_ALLOW(munmap), 119 SC_ALLOW(exit_group), 120 #ifdef __NR_rt_sigprocmask 121 SC_ALLOW(rt_sigprocmask), 122 #else 123 SC_ALLOW(sigprocmask), 124 #endif 125 BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), 126 }; 127 128 static const struct sock_fprog preauth_program = { 129 .len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])), 130 .filter = (struct sock_filter *)preauth_insns, 131 }; 132 133 struct ssh_sandbox { 134 pid_t child_pid; 135 }; 136 137 struct ssh_sandbox * 138 ssh_sandbox_init(struct monitor *monitor) 139 { 140 struct ssh_sandbox *box; 141 142 /* 143 * Strictly, we don't need to maintain any state here but we need 144 * to return non-NULL to satisfy the API. 145 */ 146 debug3("%s: preparing seccomp filter sandbox", __func__); 147 box = xcalloc(1, sizeof(*box)); 148 box->child_pid = 0; 149 150 return box; 151 } 152 153 #ifdef SANDBOX_SECCOMP_FILTER_DEBUG 154 extern struct monitor *pmonitor; 155 void mm_log_handler(LogLevel level, const char *msg, void *ctx); 156 157 static void 158 ssh_sandbox_violation(int signum, siginfo_t *info, void *void_context) 159 { 160 char msg[256]; 161 162 snprintf(msg, sizeof(msg), 163 "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)", 164 __func__, info->si_arch, info->si_syscall, info->si_call_addr); 165 mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor); 166 _exit(1); 167 } 168 169 static void 170 ssh_sandbox_child_debugging(void) 171 { 172 struct sigaction act; 173 sigset_t mask; 174 175 debug3("%s: installing SIGSYS handler", __func__); 176 memset(&act, 0, sizeof(act)); 177 sigemptyset(&mask); 178 sigaddset(&mask, SIGSYS); 179 180 act.sa_sigaction = &ssh_sandbox_violation; 181 act.sa_flags = SA_SIGINFO; 182 if (sigaction(SIGSYS, &act, NULL) == -1) 183 fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno)); 184 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) 185 fatal("%s: sigprocmask(SIGSYS): %s", 186 __func__, strerror(errno)); 187 } 188 #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 189 190 void 191 ssh_sandbox_child(struct ssh_sandbox *box) 192 { 193 struct rlimit rl_zero; 194 int nnp_failed = 0; 195 196 /* Set rlimits for completeness if possible. */ 197 rl_zero.rlim_cur = rl_zero.rlim_max = 0; 198 if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) 199 fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", 200 __func__, strerror(errno)); 201 if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) 202 fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", 203 __func__, strerror(errno)); 204 if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) 205 fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", 206 __func__, strerror(errno)); 207 208 #ifdef SANDBOX_SECCOMP_FILTER_DEBUG 209 ssh_sandbox_child_debugging(); 210 #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ 211 212 debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__); 213 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { 214 debug("%s: prctl(PR_SET_NO_NEW_PRIVS): %s", 215 __func__, strerror(errno)); 216 nnp_failed = 1; 217 } 218 debug3("%s: attaching seccomp filter program", __func__); 219 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1) 220 debug("%s: prctl(PR_SET_SECCOMP): %s", 221 __func__, strerror(errno)); 222 else if (nnp_failed) 223 fatal("%s: SECCOMP_MODE_FILTER activated but " 224 "PR_SET_NO_NEW_PRIVS failed", __func__); 225 } 226 227 void 228 ssh_sandbox_parent_finish(struct ssh_sandbox *box) 229 { 230 free(box); 231 debug3("%s: finished", __func__); 232 } 233 234 void 235 ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) 236 { 237 box->child_pid = child_pid; 238 } 239 240 #endif /* SANDBOX_SECCOMP_FILTER */ 241