1 /* $OpenBSD: sandbox-systrace.c,v 1.4 2011/07/29 14:42:45 djm Exp $ */ 2 /* 3 * Copyright (c) 2011 Damien Miller <djm@mindrot.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "includes.h" 19 20 #ifdef SANDBOX_SYSTRACE 21 22 #include <sys/types.h> 23 #include <sys/param.h> 24 #include <sys/ioctl.h> 25 #include <sys/syscall.h> 26 #include <sys/socket.h> 27 28 #include <dev/systrace.h> 29 30 #include <errno.h> 31 #include <fcntl.h> 32 #include <limits.h> 33 #include <stdarg.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "atomicio.h" 40 #include "log.h" 41 #include "ssh-sandbox.h" 42 #include "xmalloc.h" 43 44 struct sandbox_policy { 45 int syscall; 46 int action; 47 }; 48 49 /* Permitted syscalls in preauth. Unlisted syscalls get SYSTR_POLICY_KILL */ 50 static const struct sandbox_policy preauth_policy[] = { 51 { SYS_open, SYSTR_POLICY_NEVER }, 52 53 { SYS___sysctl, SYSTR_POLICY_PERMIT }, 54 { SYS_close, SYSTR_POLICY_PERMIT }, 55 { SYS_exit, SYSTR_POLICY_PERMIT }, 56 { SYS_getpid, SYSTR_POLICY_PERMIT }, 57 { SYS_gettimeofday, SYSTR_POLICY_PERMIT }, 58 { SYS_madvise, SYSTR_POLICY_PERMIT }, 59 { SYS_mmap, SYSTR_POLICY_PERMIT }, 60 { SYS_mprotect, SYSTR_POLICY_PERMIT }, 61 { SYS_poll, SYSTR_POLICY_PERMIT }, 62 { SYS_munmap, SYSTR_POLICY_PERMIT }, 63 { SYS_read, SYSTR_POLICY_PERMIT }, 64 { SYS_select, SYSTR_POLICY_PERMIT }, 65 { SYS_sigprocmask, SYSTR_POLICY_PERMIT }, 66 { SYS_write, SYSTR_POLICY_PERMIT }, 67 { -1, -1 } 68 }; 69 70 struct ssh_sandbox { 71 int child_sock; 72 int parent_sock; 73 int systrace_fd; 74 pid_t child_pid; 75 }; 76 77 struct ssh_sandbox * 78 ssh_sandbox_init(void) 79 { 80 struct ssh_sandbox *box; 81 int s[2]; 82 83 debug3("%s: preparing systrace sandbox", __func__); 84 box = xcalloc(1, sizeof(*box)); 85 if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) 86 fatal("%s: socketpair: %s", __func__, strerror(errno)); 87 box->child_sock = s[0]; 88 box->parent_sock = s[1]; 89 box->systrace_fd = -1; 90 box->child_pid = 0; 91 92 return box; 93 } 94 95 void 96 ssh_sandbox_child(struct ssh_sandbox *box) 97 { 98 char whatever = 0; 99 100 close(box->parent_sock); 101 /* Signal parent that we are ready */ 102 debug3("%s: ready", __func__); 103 if (atomicio(vwrite, box->child_sock, &whatever, 1) != 1) 104 fatal("%s: write: %s", __func__, strerror(errno)); 105 /* Wait for parent to signal for us to go */ 106 if (atomicio(read, box->child_sock, &whatever, 1) != 1) 107 fatal("%s: read: %s", __func__, strerror(errno)); 108 debug3("%s: started", __func__); 109 close(box->child_sock); 110 } 111 112 static void 113 ssh_sandbox_parent(struct ssh_sandbox *box, pid_t child_pid, 114 const struct sandbox_policy *allowed_syscalls) 115 { 116 int dev_systrace, i, j, found; 117 char whatever = 0; 118 struct systrace_policy policy; 119 120 debug3("%s: wait for child %ld", __func__, (long)child_pid); 121 box->child_pid = child_pid; 122 close(box->child_sock); 123 /* Wait for child to signal that it is ready */ 124 if (atomicio(read, box->parent_sock, &whatever, 1) != 1) 125 fatal("%s: read: %s", __func__, strerror(errno)); 126 debug3("%s: child %ld ready", __func__, (long)child_pid); 127 128 /* Set up systracing of child */ 129 if ((dev_systrace = open("/dev/systrace", O_RDONLY)) == -1) 130 fatal("%s: open(\"/dev/systrace\"): %s", __func__, 131 strerror(errno)); 132 if (ioctl(dev_systrace, STRIOCCLONE, &box->systrace_fd) == -1) 133 fatal("%s: ioctl(STRIOCCLONE, %d): %s", __func__, 134 dev_systrace, strerror(errno)); 135 close(dev_systrace); 136 debug3("%s: systrace attach, fd=%d", __func__, box->systrace_fd); 137 if (ioctl(box->systrace_fd, STRIOCATTACH, &child_pid) == -1) 138 fatal("%s: ioctl(%d, STRIOCATTACH, %d): %s", __func__, 139 box->systrace_fd, child_pid, strerror(errno)); 140 141 /* Allocate and assign policy */ 142 bzero(&policy, sizeof(policy)); 143 policy.strp_op = SYSTR_POLICY_NEW; 144 policy.strp_maxents = SYS_MAXSYSCALL; 145 if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) 146 fatal("%s: ioctl(%d, STRIOCPOLICY (new)): %s", __func__, 147 box->systrace_fd, strerror(errno)); 148 149 policy.strp_op = SYSTR_POLICY_ASSIGN; 150 policy.strp_pid = box->child_pid; 151 if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) 152 fatal("%s: ioctl(%d, STRIOCPOLICY (assign)): %s", 153 __func__, box->systrace_fd, strerror(errno)); 154 155 /* Set per-syscall policy */ 156 for (i = 0; i < SYS_MAXSYSCALL; i++) { 157 found = 0; 158 for (j = 0; allowed_syscalls[j].syscall != -1; j++) { 159 if (allowed_syscalls[j].syscall == i) { 160 found = 1; 161 break; 162 } 163 } 164 policy.strp_op = SYSTR_POLICY_MODIFY; 165 policy.strp_code = i; 166 policy.strp_policy = found ? 167 allowed_syscalls[j].action : SYSTR_POLICY_KILL; 168 if (found) 169 debug3("%s: policy: enable syscall %d", __func__, i); 170 if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) 171 fatal("%s: ioctl(%d, STRIOCPOLICY (modify)): %s", 172 __func__, box->systrace_fd, strerror(errno)); 173 } 174 175 /* Signal the child to start running */ 176 debug3("%s: start child %ld", __func__, (long)child_pid); 177 if (atomicio(vwrite, box->parent_sock, &whatever, 1) != 1) 178 fatal("%s: write: %s", __func__, strerror(errno)); 179 close(box->parent_sock); 180 } 181 182 void 183 ssh_sandbox_parent_finish(struct ssh_sandbox *box) 184 { 185 /* Closing this before the child exits will terminate it */ 186 close(box->systrace_fd); 187 188 free(box); 189 debug3("%s: finished", __func__); 190 } 191 192 void 193 ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) 194 { 195 ssh_sandbox_parent(box, child_pid, preauth_policy); 196 } 197 198 #endif /* SANDBOX_SYSTRACE */ 199