Example #1
0
/*
 * Initialise the child context of a sandbox.
 * Each sandbox will want to do something here to make sure that the
 * child context is sandboxed properly.
 * This function depends on "type": if SAND_WORKER, we set fd1 to be the
 * descriptor between the child and the application; if fd2 isn't -1,
 * it's the FastCGI control connection (fdfiled and fdaccept should be
 * ignored in SAND_WORKER case).
 * If not SAND_WORKER, we're the control process in a FastCGI context:
 * fd1 is the control connection; fd2 is -1; fdaccept, if not -1, is the
 * old-style FastCGI socket; fdfiled, if not -1, is the new-style
 * transport descriptor interface.
 * Whew!
 */
int
ksandbox_init_child(void *arg, 
	enum sandtype type, int fd1, int fd2,
	int fdfiled, int fdaccept)
{

#if defined(HAVE_CAPSICUM)
	if ( ! ksandbox_capsicum_init_child(arg, type, 
	    fd1, fd2, fdfiled, fdaccept)) {
		XWARNX("ksandbox_capsicum_init_child");
		return(0);
	}
#elif defined(HAVE_SANDBOX_INIT)
	if ( ! ksandbox_darwin_init_child(arg, type)) {
		XWARNX("ksandbox_darwin_init_child");
		return(0);
	}
#elif defined(HAVE_PLEDGE)
	if ( ! ksandbox_pledge_init_child(arg, type)) {
		XWARNX("ksandbox_pledge_init_child");
		return(0);
	}
#elif defined(HAVE_SYSTRACE)
	if ( ! ksandbox_systrace_init_child(arg, type)) {
		XWARNX("ksandbox_systrace_init_child");
		return(0);
	}
#elif defined(HAVE_SECCOMP_FILTER)
	if ( ! ksandbox_seccomp_init_child(arg, type)) {
		XWARNX("ksandbox_seccomp_init_child");
		return(0);
	}
#endif
	return(1);
}
Example #2
0
static int
ksandbox_capsicum_init_control(void *arg, int fd1, int fd2)
{
	int rc;
	struct rlimit	 rl_zero;
	cap_rights_t	 rights;

	cap_rights_init(&rights);

	cap_rights_init(&rights, CAP_EVENT, CAP_FCNTL, CAP_ACCEPT);
	if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && 
		 errno != ENOSYS) {
 		XWARN("cap_rights_limit: STDIN_FILENO");
		return(0);
	}

	cap_rights_init(&rights, CAP_WRITE, CAP_FSTAT);
	if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && 
		 errno != ENOSYS) {
		XWARN("cap_rights_limit: STDERR_FILENO");
		return(0);
	}

	cap_rights_init(&rights, CAP_EVENT, 
		CAP_FCNTL, CAP_READ, CAP_WRITE);
	if (cap_rights_limit(fd1, &rights) < 0 && 
		 errno != ENOSYS) {
		XWARN("cap_rights_limit: internal socket");
		return(0);
	}

	rl_zero.rlim_cur = rl_zero.rlim_max = 0;

	if (-1 == setrlimit(RLIMIT_FSIZE, &rl_zero)) {
		XWARNX("setrlimit: rlimit_fsize");
		return(0);
	} else if (-1 == setrlimit(RLIMIT_NPROC, &rl_zero)) {
		XWARNX("setrlimit: rlimit_nproc");
		return(0);
	}

	rc = cap_enter();
	if (0 != rc && errno != ENOSYS) {
		XWARN("cap_enter");
		rc = 0;
	} else
		rc = 1;

	return(rc);
}
Example #3
0
int
ksandbox_capsicum_init_child(void *arg, 
	enum sandtype type, int fd1, int fd2,
	int fdfiled, int fdaccept)
{
	int	 rc;

	switch (type) {
	case (SAND_WORKER):
		rc = ksandbox_capsicum_init_worker(arg, fd1, fd2);
		break;
	case (SAND_CONTROL_OLD):
		assert(-1 == fd2);
		rc = ksandbox_capsicum_init_control
			(arg, fd1, fdfiled, fdaccept);
		break;
	case (SAND_CONTROL_NEW):
		assert(-1 == fd2);
		rc = ksandbox_capsicum_init_control
			(arg, fd1, fdfiled, fdaccept);
		break;
	default:
		abort();
	}

	if ( ! rc) 
		XWARNX("capsicum sandbox failure");
	return(rc);
}
Example #4
0
void
ksandbox_systrace_close(void *arg)
{
    struct systrace_sandbox *box = arg;

    /* Closing this before the child exits will terminate it */
    if (-1 != box->systrace_fd)
        close(box->systrace_fd);
    else
        XWARNX("systrace fd not opened");
}
Example #5
0
int
ksandbox_systrace_init_child(void *arg, enum sandtype type)
{
    struct systrace_sandbox *box = arg;

    if (NULL == arg) {
        XWARNX("systrace child passed null config");
        return(0);
    }

    signal(SIGCHLD, box->osigchld);
    if (kill(getpid(), SIGSTOP) == 0)
        return(1);

    XWARN("kill: SIGSTOP");
    return(0);
}
Example #6
0
/*
 * Perform system-specific initialisation for the parent.
 * This is only used by systrace(4), which requires the parent to
 * register the child's systrace hooks.
 */
int
ksandbox_init_parent(void *arg, enum sandtype type, pid_t child)
{

#if defined(HAVE_CAPSICUM)
	return(1);
#elif defined(HAVE_SANDBOX_INIT)
	return(1);
#elif defined(HAVE_PLEDGE)
	return(1);
#elif defined(HAVE_SYSTRACE)
	if ( ! ksandbox_systrace_init_parent(arg, type, child)) {
		XWARNX("ksandbox_systrace_init_child");
		return(0);
	}
#elif defined(HAVE_SECCOMP_FILTER)
	return(1);
#endif
	return(1);
}
int
ksandbox_seccomp_init_child(void *arg, enum sandtype type)
{
	struct rlimit rl_zero;
	int nnp_failed = 0;

	/* Set rlimits for completeness if possible. */
	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
	if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1)
		XWARN("setrlimit(RLIMIT_FSIZE)");
#if 0
	/*
	 * Don't do like OpenSSH: we need to pass stuff back and forth
	 * over pipes, and this will prevent that from happening.
	 */
	if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1)
		XWARN("setrlimit(RLIMIT_NOFILE)");
#endif
	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
		XWARN("setrlimit(RLIMIT_NPROC)");

#ifdef SANDBOX_SECCOMP_DEBUG
	ssh_sandbox_child_debugging();
#endif 

	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
		XWARN("prctl(PR_SET_NO_NEW_PRIVS)");
		nnp_failed = 1;
	}
	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, 
		 SAND_WORKER != type ?
		 &preauth_prog_ctrl :
		 &preauth_prog_work) == -1)
		XWARN("prctl(PR_SET_SECCOMP)");
	else if (nnp_failed) {
		XWARNX("SECCOMP_MODE_FILTER activated but "
		    "PR_SET_NO_NEW_PRIVS failed");
		_exit(EXIT_FAILURE);
	}
	return(1);
}
Example #8
0
/*
 * Allocate system-specific data for the sandbox.
 * This is only used by systrace, which requires extra accounting
 * information for the child.
 */
int
ksandbox_alloc(void **pp)
{

	*pp = NULL;

#if defined(HAVE_CAPSICUM)
	return(1);
#elif defined(HAVE_SANDBOX_INIT)
	return(1);
#elif defined(HAVE_PLEDGE)
	return(1);
#elif defined(HAVE_SYSTRACE)
	if (NULL == (*pp = (ksandbox_systrace_alloc()))) {
		XWARNX("ksandbox_systrace_alloc");
		return(0);
	}
#elif defined(HAVE_SECCOMP_FILTER)
	return(1);
#endif
	return(1);
}
Example #9
0
int
ksandbox_darwin_init_child(void *arg, enum sandtype type)
{
	int	 	 rc;
	char		*er;
	struct rlimit	 rl_zero;

	rc = SAND_WORKER == type ?
		sandbox_init(kSBXProfilePureComputation, 
			SANDBOX_NAMED, &er) :
		sandbox_init(kSBXProfileNoWrite, 
			SANDBOX_NAMED, &er);

	if (0 != rc) {
		XWARNX("sandbox_init: %s", er);
		sandbox_free_error(er);
		rc = 0;
	} else
		rc = 1;

	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
#if 0
	/*
	 * FIXME: I've taken out the RLIMIT_NOFILE setrlimit() because
	 * it causes strange behaviour.  On Mac OS X, it fails with
	 * EPERM no matter what (the same code runs fine when not run as
	 * a CGI instance).
	 */
	if (-1 == setrlimit(RLIMIT_NOFILE, &rl_zero))
		XWARN("setrlimit: rlimit_fsize");
#endif
	if (-1 == setrlimit(RLIMIT_FSIZE, &rl_zero))
		XWARN("setrlimit: rlimit_fsize");
	if (-1 == setrlimit(RLIMIT_NPROC, &rl_zero))
		XWARN("setrlimit: rlimit_nproc");

	return(rc);
}
Example #10
0
/*
 * Read a single kpair from the child.
 * This returns 0 if there are no more pairs to read and -1 if any
 * errors occur (the parent should also exit with server failure).
 * Otherwise, it returns 1 and the pair is zeroed and filled in.
 */
static int
input(enum input *type, struct kpair *kp, 
	int fd, enum kcgi_err *ke, int eofok)
{
	size_t		 sz;
	int		 rc;
	ptrdiff_t	 diff;

	memset(kp, 0, sizeof(struct kpair));

	/* This will return EOF for the last one. */
	rc = fullread(fd, type, sizeof(enum input), 1, ke);
	if (0 == rc) {
		if (eofok) 
			return(0);
		XWARNX("unexpected eof from child");
		*ke = KCGI_FORM;
		return(-1);
	} else if (rc < 0)
		return(-1);

	if (*type == IN__MAX)
		return(0);

	if (*type > IN__MAX) {
		XWARNX("unknown input type %d", *type);
		*ke = KCGI_FORM;
		return(-1);
	}

	/* TODO: check additive overflow. */
	if (fullread(fd, &sz, sizeof(size_t), 0, ke) < 0)
		return(-1);
	if (NULL == (kp->key = XCALLOC(sz + 1, 1))) {
		*ke = KCGI_ENOMEM;
		return(-1);
	}
	if (fullread(fd, kp->key, sz, 0, ke) < 0)
		return(-1);

	/* TODO: check additive overflow. */
	if (fullread(fd, &kp->valsz, sizeof(size_t), 0, ke) < 0)
		return(-1);
	if (NULL == (kp->val = XCALLOC(kp->valsz + 1, 1))) {
		*ke = KCGI_ENOMEM;
		return(-1);
	}
	if (fullread(fd, kp->val, kp->valsz, 0, ke) < 0)
		return(-1);

	if (fullread(fd, &kp->state, sizeof(enum kpairstate), 0, ke) < 0)
		return(-1);
	if (fullread(fd, &kp->type, sizeof(enum kpairtype), 0, ke) < 0)
		return(-1);
	if (fullread(fd, &kp->keypos, sizeof(size_t), 0, ke) < 0)
		return(-1);

	if (KPAIR_VALID == kp->state)
		switch (kp->type) {
		case (KPAIR_DOUBLE):
			if (fullread(fd, &kp->parsed.d, sizeof(double), 0, ke) < 0)
				return(-1);
			break;
		case (KPAIR_INTEGER):
			if (fullread(fd, &kp->parsed.i, sizeof(int64_t), 0, ke) < 0)
				return(-1);
			break;
		case (KPAIR_STRING):
			if (fullread(fd, &diff, sizeof(ptrdiff_t), 0, ke) < 0)
				return(-1);
			if (diff > (ssize_t)kp->valsz) {
				*ke = KCGI_FORM;
				return(-1);
			}
			kp->parsed.s = kp->val + diff;
			break;
		default:
			break;
		}

	/* TODO: check additive overflow. */
	if (fullread(fd, &sz, sizeof(size_t), 0, ke) < 0)
		return(-1);
	if (NULL == (kp->file = XCALLOC(sz + 1, 1))) {
		*ke = KCGI_ENOMEM;
		return(-1);
	}
	if (fullread(fd, kp->file, sz, 0, ke) < 0)
		return(-1);

	/* TODO: check additive overflow. */
	if (fullread(fd, &sz, sizeof(size_t), 0, ke) < 0)
		return(-1);
	if (NULL == (kp->ctype = XCALLOC(sz + 1, 1))) {
		*ke = KCGI_ENOMEM;
		return(-1);
	}
	if (fullread(fd, kp->ctype, sz, 0, ke) < 0)
		return(-1);
	if (fullread(fd, &kp->ctypepos, sizeof(size_t), 0, ke) < 0)
		return(-1);

	/* TODO: check additive overflow. */
	if (fullread(fd, &sz, sizeof(size_t), 0, ke) < 0)
		return(-1);
	if (NULL == (kp->xcode = XCALLOC(sz + 1, 1)))  {
		*ke = KCGI_ENOMEM;
		return(-1);
	}
	if (fullread(fd, kp->xcode, sz, 0, ke) < 0) 
		return(-1);

	return(1);
}
Example #11
0
/*
 * This is the parent kcgi process.
 * It spins on input from the child until all fields have been received.
 * These fields are sent from the child's output() function.
 * Each input field consists of the data and its validation state.
 * We build up the kpair arrays here with this data, then assign the
 * kpairs into named buckets.
 */
enum kcgi_err
kworker_parent(int fd, struct kreq *r, int eofok)
{
	struct kpair	 kp;
	struct kpair	*kpp;
	enum krequ	 requ;
	enum input	 type;
	int		 rc;
	enum kcgi_err	 ke;
	size_t		 i, dgsz;

	/* Pointers freed at "out" label. */
	memset(&kp, 0, sizeof(struct kpair));

	/*
	 * First read all of our parsed parameters.
	 * Each parsed parameter is handled a little differently.
	 * This list will end with META__MAX.
	 */
	if (fullread(fd, &r->reqsz, sizeof(size_t), 0, &ke) < 0) {
		XWARNX("failed to read request header size");
		goto out;
	}
	r->reqs = XCALLOC(r->reqsz, sizeof(struct khead));
	if (NULL == r->reqs) {
		ke = KCGI_ENOMEM;
		goto out;
	}
	for (i = 0; i < r->reqsz; i++) {
		if (fullread(fd, &requ, sizeof(enum krequ), 0, &ke) < 0) {
			XWARNX("failed to read request identifier");
			goto out;
		}
		if (KCGI_OK != (ke = fullreadword(fd, &r->reqs[i].key))) {
			XWARNX("failed to read request key");
			goto out;
		}
		if (KCGI_OK != (ke = fullreadword(fd, &r->reqs[i].val))) {
			XWARNX("failed to read request value");
			goto out;
		}
		if (requ != KREQU__MAX)
			r->reqmap[requ] = &r->reqs[i];
	}

	if (fullread(fd, &r->method, sizeof(enum kmethod), 0, &ke) < 0) {
		XWARNX("failed to read request method");
		goto out;
	} else if (fullread(fd, &r->auth, sizeof(enum kauth), 0, &ke) < 0) {
		XWARNX("failed to read authorisation type");
		goto out;
	} else if (KCGI_OK != (ke = kworker_auth_parent(fd, &r->rawauth))) {
		XWARNX("failed to read raw authorisation");
		goto out;
	} else if (fullread(fd, &r->scheme, sizeof(enum kscheme), 0, &ke) < 0) {
		XWARNX("failed to read scheme");
		goto out;
	} else if (KCGI_OK != (ke = fullreadword(fd, &r->remote))) {
		XWARNX("failed to read remote");
		goto out;
	} else if (KCGI_OK != (ke = fullreadword(fd, &r->fullpath))) {
		XWARNX("failed to read fullpath");
		goto out;
	} else if (KCGI_OK != (ke = fullreadword(fd, &r->suffix))) {
		XWARNX("failed to read suffix");
		goto out;
	} else if (KCGI_OK != (ke = fullreadword(fd, &r->pagename))) {
		XWARNX("failed to read page part");
		goto out;
	} else if (KCGI_OK != (ke = fullreadword(fd, &r->path))) {
		XWARNX("failed to read path part");
		goto out;
	} else if (KCGI_OK != (ke = fullreadword(fd, &r->pname))) {
		XWARNX("failed to read script name");
		goto out;
	} else if (KCGI_OK != (ke = fullreadword(fd, &r->host))) {
		XWARNX("failed to read host name");
		goto out;
	} else if (fullread(fd, &r->port, sizeof(uint16_t), 0, &ke) < 0) {
		XWARNX("failed to read port");
		goto out;
	} else if (fullread(fd, &dgsz, sizeof(size_t), 0, &ke) < 0) {
		XWARNX("failed to read digest length");
		goto out;
	} else if (MD5_DIGEST_LENGTH == dgsz) {
		/* This is a binary value. */
		if (NULL == (r->rawauth.digest = XMALLOC(dgsz)))
			goto out;
		if (fullread(fd, r->rawauth.digest, dgsz, 0, &ke) < 0) {
			XWARNX("failed to read digest");
			goto out;
		}
	}

	while ((rc = input(&type, &kp, fd, &ke, eofok)) > 0) {
		assert(type < IN__MAX);
		/*
		 * We have a parsed field from the child process.
		 * Begin by expanding the number of parsed fields
		 * depending on whether we have a cookie or form input.
		 * Then copy the new data.
		 */
		kpp = IN_COOKIE == type ?
			kpair_expand(&r->cookies, &r->cookiesz) :
			kpair_expand(&r->fields, &r->fieldsz);

		if (NULL == kpp) {
			rc = -1;
			ke = KCGI_ENOMEM;
			break;
		}

		*kpp = kp;
	}

	if (rc < 0)
		goto out;

	/*
	 * Now that the field and cookie arrays are fixed and not going
	 * to be reallocated any more, we run through both arrays and
	 * assign the named fields into buckets.
	 */
	for (i = 0; i < r->fieldsz; i++) {
		kpp = &r->fields[i];
		if (kpp->keypos == r->keysz)
			continue;
		if (KPAIR_INVALID != kpp->state) {
			kpp->next = r->fieldmap[kpp->keypos];
			r->fieldmap[kpp->keypos] = kpp;
		} else {
			kpp->next = r->fieldnmap[kpp->keypos];
			r->fieldnmap[kpp->keypos] = kpp;
		}
	}
	for (i = 0; i < r->cookiesz; i++) {
		kpp = &r->cookies[i];
		if (kpp->keypos == r->keysz)
			continue;
		if (KPAIR_INVALID != kpp->state) {
			kpp->next = r->cookiemap[kpp->keypos];
			r->cookiemap[kpp->keypos] = kpp;
		} else {
			kpp->next = r->cookienmap[kpp->keypos];
			r->cookienmap[kpp->keypos] = kpp;
		}
	}

	ke = KCGI_OK;
	/*
	 * Usually, "kp" would be zeroed after its memory is copied into
	 * one of the form-input arrays.
	 * However, in the case of error, these may still have
	 * allocations, so free them now.
	 */
out:
	free(kp.key);
	free(kp.val);
	free(kp.file);
	free(kp.ctype);
	free(kp.xcode);
	return(ke);
}
Example #12
0
static int
ksandbox_capsicum_init_control(void *arg, 
	int worker, int fdfiled, int fdaccept)
{
	int rc;
	struct rlimit	 rl_zero;
	cap_rights_t	 rights;

	cap_rights_init(&rights);

	if (-1 != fdaccept) {
		/*
		 * If we have old-style accept FastCGI sockets, then
		 * mark us as accepting on it.
		 */
		cap_rights_init(&rights, 
			CAP_EVENT, CAP_FCNTL, CAP_ACCEPT);
		if (cap_rights_limit(fdaccept, &rights) < 0 && 
			 errno != ENOSYS) {
			XWARN("cap_rights_limit: accept socket");
			return(0);
		}
	} else {
		/* New-style descriptor-passing socket. */
		assert(-1 != fdfiled);
		cap_rights_init(&rights, CAP_EVENT, 
			CAP_FCNTL, CAP_READ, CAP_WRITE);
		if (cap_rights_limit(fdfiled, &rights) < 0 && 
			 errno != ENOSYS) {
			XWARN("cap_rights_limit: descriptor socket");
			return(0);
		}
	}

	/* Always pass through write-only stderr. */
	cap_rights_init(&rights, CAP_WRITE, CAP_FSTAT);
	if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && 
		 errno != ENOSYS) {
		XWARN("cap_rights_limit: STDERR_FILENO");
		return(0);
	}

	/* Interface to worker. */
	cap_rights_init(&rights, CAP_EVENT, 
		CAP_FCNTL, CAP_READ, CAP_WRITE);
	if (cap_rights_limit(worker, &rights) < 0 && 
		 errno != ENOSYS) {
		XWARN("cap_rights_limit: internal socket");
		return(0);
	}

	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
	if (-1 == setrlimit(RLIMIT_FSIZE, &rl_zero)) {
		XWARNX("setrlimit: rlimit_fsize");
		return(0);
	} else if (-1 == setrlimit(RLIMIT_NPROC, &rl_zero)) {
		XWARNX("setrlimit: rlimit_nproc");
		return(0);
	}

	rc = cap_enter();
	if (0 != rc && errno != ENOSYS) {
		XWARN("cap_enter");
		rc = 0;
	} else
		rc = 1;

	return(rc);
}
Example #13
0
static int
ksandbox_capsicum_init_worker(void *arg, int fd1, int fd2)
{
	int rc;
	struct rlimit	 rl_zero;
	cap_rights_t	 rights;

	cap_rights_init(&rights);

	/* 
	 * Test for EBADF because STDIN_FILENO is usually closed by the
	 * caller.
	 */
	cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_FSTAT);
	if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && 
		 errno != ENOSYS && errno != EBADF) {
 		XWARN("cap_rights_limit: STDIN_FILENO");
		return(0);
	}

	cap_rights_init(&rights, CAP_EVENT, CAP_WRITE, CAP_FSTAT);
	if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && 
		 errno != ENOSYS) {
		XWARN("cap_rights_limit: STDERR_FILENO");
		return(0);
	}

	cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE, CAP_FSTAT);
	if (-1 != fd1 && cap_rights_limit(fd1, &rights) < 0 && 
		 errno != ENOSYS) {
		XWARN("cap_rights_limit: internal socket");
		return(0);
	}
	if (-1 != fd2 && cap_rights_limit(fd2, &rights) < 0 && 
		 errno != ENOSYS) {
		XWARN("cap_rights_limit: internal socket");
		return(0);
	}

	rl_zero.rlim_cur = rl_zero.rlim_max = 0;

	if (-1 == setrlimit(RLIMIT_NOFILE, &rl_zero)) {
		XWARNX("setrlimit: rlimit_fsize");
		return(0);
	} else if (-1 == setrlimit(RLIMIT_FSIZE, &rl_zero)) {
		XWARNX("setrlimit: rlimit_fsize");
		return(0);
	} else if (-1 == setrlimit(RLIMIT_NPROC, &rl_zero)) {
		XWARNX("setrlimit: rlimit_nproc");
		return(0);
	}

	rc = cap_enter();
	if (0 != rc && errno != ENOSYS) {
		XWARN("cap_enter");
		rc = 0;
	} else
		rc = 1;

	return(rc);
}
Example #14
0
int
ksandbox_systrace_init_parent(void *arg, enum sandtype type, pid_t child)
{
    struct systrace_sandbox *box = arg;
    int		dev, i, j, found, st, rc;
    pid_t		pid;
    struct systrace_policy policy;
    const struct systrace_preauth *preauth;

    assert(NULL != arg);

    preauth = SAND_WORKER != type ?
              preauth_control : preauth_worker;

    rc = 0;

    /*
     * Wait for the child to send itself a SIGSTOP.
     * When we receive it, the child is waiting to be sandboxed.
     */
    do
        pid = waitpid(child, &st, WUNTRACED);
    while (pid == -1 && errno == EINTR);

    if (-1 == pid) {
        XWARN("waitpid");
        return(0);
    }

    /* Catch if it exits. */
    signal(SIGCHLD, box->osigchld);

    if ( ! WIFSTOPPED(st)) {
        if (WIFSIGNALED(st)) {
            XWARNX("child signal %d", WTERMSIG(st));
            return(0);
        } else if (WIFEXITED(st)) {
            XWARNX("child exit %d", WEXITSTATUS(st));
            return(0);
        }
        XWARNX("child not stopped");
        return(0);
    }

    box->child_pid = child;

    /* Set up systracing of child */
    if ((dev = open("/dev/systrace", O_RDONLY)) == -1) {
        if (ENXIO == errno) {
            XWARN("open: /dev/systrace (mounted nodev?)");
            goto out;
        }
        XWARN("open: /dev/systrace");
        goto out;
    }

    if (ioctl(dev, STRIOCCLONE, &box->systrace_fd) == -1) {
        XWARN("ioctl: STRIOCCLONE");
        close(dev);
        goto out;
    }

    close(dev);

    if (ioctl(box->systrace_fd, STRIOCATTACH, &child) == -1) {
        XWARN("ioctl: STRIOCATTACH");
        goto out;
    }

    /* Allocate and assign policy */
    memset(&policy, 0, sizeof(policy));
    policy.strp_op = SYSTR_POLICY_NEW;
    policy.strp_maxents = SYS_MAXSYSCALL;
    if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) {
        XWARN("ioctl: STRIOCPOLICY (new)");
        goto out;
    }

    policy.strp_op = SYSTR_POLICY_ASSIGN;
    policy.strp_pid = box->child_pid;
    if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) {
        XWARN("ioctl: STRIOCPOLICY (assign)");
        goto out;
    }

    /* Set per-syscall policy */
    for (i = 0; i < SYS_MAXSYSCALL; i++) {
        found = 0;
        for (j = 0; preauth[j].syscall != -1; j++) {
            if (preauth[j].syscall == i) {
                found = 1;
                break;
            }
        }
        policy.strp_op = SYSTR_POLICY_MODIFY;
        policy.strp_code = i;
        policy.strp_policy = found ?
                             preauth[j].action : SYSTR_POLICY_KILL;
        if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) {
            XWARN("ioctl: STRIOCPOLICY (modify)");
            goto out;
        }
    }

    rc = 1;
out:
    /* Signal the child to start running */
    if (kill(box->child_pid, SIGCONT) == 0)
        return(rc);

    XWARN("kill: SIGCONT");
    return(0);
}