Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: sandbox-systrace.c,v 1.4 2011/07/29 14:42:45 djm Exp $ */
      2 /*
      3  * Copyright (c) 2011 Damien Miller <djm (at) 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