xref: /freebsd-src/usr.sbin/jail/command.c (revision 5cf705491727dd963485f9911ee3d52c3bf148db)
12671ee73SJamie Gritton /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4d8352076SJamie Gritton  * Copyright (c) 2011 James Gritton
52671ee73SJamie Gritton  * All rights reserved.
62671ee73SJamie Gritton  *
72671ee73SJamie Gritton  * Redistribution and use in source and binary forms, with or without
82671ee73SJamie Gritton  * modification, are permitted provided that the following conditions
92671ee73SJamie Gritton  * are met:
102671ee73SJamie Gritton  * 1. Redistributions of source code must retain the above copyright
112671ee73SJamie Gritton  *    notice, this list of conditions and the following disclaimer.
122671ee73SJamie Gritton  * 2. Redistributions in binary form must reproduce the above copyright
132671ee73SJamie Gritton  *    notice, this list of conditions and the following disclaimer in the
142671ee73SJamie Gritton  *    documentation and/or other materials provided with the distribution.
152671ee73SJamie Gritton  *
162671ee73SJamie Gritton  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172671ee73SJamie Gritton  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182671ee73SJamie Gritton  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192671ee73SJamie Gritton  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202671ee73SJamie Gritton  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212671ee73SJamie Gritton  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222671ee73SJamie Gritton  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232671ee73SJamie Gritton  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242671ee73SJamie Gritton  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252671ee73SJamie Gritton  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262671ee73SJamie Gritton  * SUCH DAMAGE.
272671ee73SJamie Gritton  */
282671ee73SJamie Gritton 
292671ee73SJamie Gritton #include <sys/types.h>
30466df976SKyle Evans #include <sys/cpuset.h>
312671ee73SJamie Gritton #include <sys/event.h>
325264032fSJamie Gritton #include <sys/mount.h>
332671ee73SJamie Gritton #include <sys/stat.h>
342671ee73SJamie Gritton #include <sys/sysctl.h>
352671ee73SJamie Gritton #include <sys/user.h>
362671ee73SJamie Gritton #include <sys/wait.h>
372671ee73SJamie Gritton 
382671ee73SJamie Gritton #include <err.h>
392671ee73SJamie Gritton #include <errno.h>
402671ee73SJamie Gritton #include <fcntl.h>
412671ee73SJamie Gritton #include <kvm.h>
422671ee73SJamie Gritton #include <login_cap.h>
432671ee73SJamie Gritton #include <paths.h>
442671ee73SJamie Gritton #include <pwd.h>
452671ee73SJamie Gritton #include <signal.h>
462671ee73SJamie Gritton #include <stdio.h>
472671ee73SJamie Gritton #include <stdlib.h>
482671ee73SJamie Gritton #include <string.h>
492671ee73SJamie Gritton #include <unistd.h>
50a99d8210SJamie Gritton #include <vis.h>
512671ee73SJamie Gritton 
522671ee73SJamie Gritton #include "jailp.h"
532671ee73SJamie Gritton 
542671ee73SJamie Gritton #define DEFAULT_STOP_TIMEOUT	10
552671ee73SJamie Gritton #define PHASH_SIZE		256
562671ee73SJamie Gritton 
572671ee73SJamie Gritton LIST_HEAD(phhead, phash);
582671ee73SJamie Gritton 
592671ee73SJamie Gritton struct phash {
602671ee73SJamie Gritton 	LIST_ENTRY(phash)	le;
612671ee73SJamie Gritton 	struct cfjail		*j;
622671ee73SJamie Gritton 	pid_t			pid;
632671ee73SJamie Gritton };
642671ee73SJamie Gritton 
65aa02af54SJamie Gritton int paralimit = -1;
66aa02af54SJamie Gritton 
672671ee73SJamie Gritton extern char **environ;
682671ee73SJamie Gritton 
693b40332cSJamie Gritton static int run_command(struct cfjail *j);
70e5935495SJamie Gritton static int add_proc(struct cfjail *j, pid_t pid);
712671ee73SJamie Gritton static void clear_procs(struct cfjail *j);
722671ee73SJamie Gritton static struct cfjail *find_proc(pid_t pid);
73aa02af54SJamie Gritton static int term_procs(struct cfjail *j);
743b40332cSJamie Gritton static int get_user_info(struct cfjail *j, const char *username,
753b40332cSJamie Gritton     const struct passwd **pwdp, login_cap_t **lcapp);
768ebbf0e2SJamie Gritton static int check_path(struct cfjail *j, const char *pname, const char *path,
775264032fSJamie Gritton     int isfile, const char *umount_type);
782671ee73SJamie Gritton 
792671ee73SJamie Gritton static struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping);
802671ee73SJamie Gritton static struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable);
81c6eff841SJamie Gritton static struct cfstring dummystring = { .len = 1 };
822671ee73SJamie Gritton static struct phhead phash[PHASH_SIZE];
832671ee73SJamie Gritton static int kq;
842671ee73SJamie Gritton 
85466df976SKyle Evans static cpusetid_t
86466df976SKyle Evans root_cpuset_id(void)
87466df976SKyle Evans {
88466df976SKyle Evans 	static cpusetid_t setid = CPUSET_INVALID;
89466df976SKyle Evans 	static int error;
90466df976SKyle Evans 
91466df976SKyle Evans 	/* Only try to get the cpuset once. */
92466df976SKyle Evans 	if (error == 0 && setid == CPUSET_INVALID)
93466df976SKyle Evans 		error = cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, &setid);
94466df976SKyle Evans 	if (error != 0)
95466df976SKyle Evans 		return (CPUSET_INVALID);
96466df976SKyle Evans 	return (setid);
97466df976SKyle Evans }
98466df976SKyle Evans 
992671ee73SJamie Gritton /*
1003b40332cSJamie Gritton  * Run the next command associated with a jail.
1012671ee73SJamie Gritton  */
1022671ee73SJamie Gritton int
1033b40332cSJamie Gritton next_command(struct cfjail *j)
1042671ee73SJamie Gritton {
1053b40332cSJamie Gritton 	enum intparam comparam;
106bea2eefcSJamie Gritton 	int create_failed, stopping;
1072671ee73SJamie Gritton 
10860080230SJamie Gritton 	if (paralimit == 0) {
1094c86c0faSJamie Gritton 		if (j->flags & JF_FROM_RUNQ)
1104c86c0faSJamie Gritton 			requeue_head(j, &runnable);
1114c86c0faSJamie Gritton 		else
11260080230SJamie Gritton 			requeue(j, &runnable);
11360080230SJamie Gritton 		return 1;
11460080230SJamie Gritton 	}
1154c86c0faSJamie Gritton 	j->flags &= ~JF_FROM_RUNQ;
1163b40332cSJamie Gritton 	create_failed = (j->flags & (JF_STOP | JF_FAILED)) == JF_FAILED;
117bea2eefcSJamie Gritton 	stopping = (j->flags & JF_STOP) != 0;
11860080230SJamie Gritton 	comparam = *j->comparam;
11960080230SJamie Gritton 	for (;;) {
1203b40332cSJamie Gritton 		if (j->comstring == NULL) {
12160080230SJamie Gritton 			j->comparam += create_failed ? -1 : 1;
12260080230SJamie Gritton 			switch ((comparam = *j->comparam)) {
1236fcbac3cSJamie Gritton 			case IP__NULL:
12460080230SJamie Gritton 				return 0;
125aa02af54SJamie Gritton 			case IP_MOUNT_DEVFS:
126aa02af54SJamie Gritton 				if (!bool_param(j->intparams[IP_MOUNT_DEVFS]))
1273b40332cSJamie Gritton 					continue;
128fbd868c9SHiroki Sato 				j->comstring = &dummystring;
129fbd868c9SHiroki Sato 				break;
130fbd868c9SHiroki Sato 			case IP_MOUNT_FDESCFS:
131fbd868c9SHiroki Sato 				if (!bool_param(j->intparams[IP_MOUNT_FDESCFS]))
132fbd868c9SHiroki Sato 					continue;
133fbd868c9SHiroki Sato 				j->comstring = &dummystring;
13407a7869fSJamie Gritton 				break;
13507a7869fSJamie Gritton 			case IP_MOUNT_PROCFS:
13607a7869fSJamie Gritton 				if (!bool_param(j->intparams[IP_MOUNT_PROCFS]))
13707a7869fSJamie Gritton 					continue;
13807a7869fSJamie Gritton 				j->comstring = &dummystring;
13907a7869fSJamie Gritton 				break;
1402b00f7baSJamie Gritton 			case IP__OP:
141aa02af54SJamie Gritton 			case IP_STOP_TIMEOUT:
1423b40332cSJamie Gritton 				j->comstring = &dummystring;
143aa02af54SJamie Gritton 				break;
144aa02af54SJamie Gritton 			default:
145aa02af54SJamie Gritton 				if (j->intparams[comparam] == NULL)
1463b40332cSJamie Gritton 					continue;
147bea2eefcSJamie Gritton 				j->comstring = create_failed || (stopping &&
148bea2eefcSJamie Gritton 				    (j->intparams[comparam]->flags & PF_REV))
1493b40332cSJamie Gritton 				    ? TAILQ_LAST(&j->intparams[comparam]->val,
1503b40332cSJamie Gritton 					cfstrings)
1513b40332cSJamie Gritton 				    : TAILQ_FIRST(&j->intparams[comparam]->val);
1522671ee73SJamie Gritton 			}
15360080230SJamie Gritton 		} else {
154c6eff841SJamie Gritton 			j->comstring = j->comstring == &dummystring ? NULL :
155bea2eefcSJamie Gritton 			    create_failed || (stopping &&
156bea2eefcSJamie Gritton 			    (j->intparams[comparam]->flags & PF_REV))
1573b40332cSJamie Gritton 			    ? TAILQ_PREV(j->comstring, cfstrings, tq)
15860080230SJamie Gritton 			    : TAILQ_NEXT(j->comstring, tq);
15960080230SJamie Gritton 		}
16060080230SJamie Gritton 		if (j->comstring == NULL || j->comstring->len == 0 ||
16160080230SJamie Gritton 		    (create_failed && (comparam == IP_EXEC_PRESTART ||
162f6c0e63bSAlexander Leidinger 		    comparam == IP_EXEC_CREATED || comparam == IP_EXEC_START ||
16366005c45SRyan Moeller 		    comparam == IP_COMMAND || comparam == IP_EXEC_POSTSTART ||
16466005c45SRyan Moeller 		    comparam == IP_EXEC_PREPARE)))
1653b40332cSJamie Gritton 			continue;
16660080230SJamie Gritton 		switch (run_command(j)) {
16760080230SJamie Gritton 		case -1:
16860080230SJamie Gritton 			failed(j);
16960080230SJamie Gritton 			/* FALLTHROUGH */
17060080230SJamie Gritton 		case 1:
1712671ee73SJamie Gritton 			return 1;
1722671ee73SJamie Gritton 		}
1732671ee73SJamie Gritton 	}
174aa02af54SJamie Gritton }
1752671ee73SJamie Gritton 
1762671ee73SJamie Gritton /*
1772671ee73SJamie Gritton  * Check command exit status
1782671ee73SJamie Gritton  */
1792671ee73SJamie Gritton int
180aa02af54SJamie Gritton finish_command(struct cfjail *j)
1812671ee73SJamie Gritton {
1824c86c0faSJamie Gritton 	struct cfjail *rj;
1832671ee73SJamie Gritton 	int error;
1842671ee73SJamie Gritton 
185aa02af54SJamie Gritton 	if (!(j->flags & JF_SLEEPQ))
1862671ee73SJamie Gritton 		return 0;
187aa02af54SJamie Gritton 	j->flags &= ~JF_SLEEPQ;
1884c86c0faSJamie Gritton 	if (*j->comparam == IP_STOP_TIMEOUT) {
189eadec913SJamie Gritton 		j->flags &= ~JF_TIMEOUT;
190eadec913SJamie Gritton 		j->pstatus = 0;
191eadec913SJamie Gritton 		return 0;
192eadec913SJamie Gritton 	}
193aa02af54SJamie Gritton 	paralimit++;
1944c86c0faSJamie Gritton 	if (!TAILQ_EMPTY(&runnable)) {
1954c86c0faSJamie Gritton 		rj = TAILQ_FIRST(&runnable);
1964c86c0faSJamie Gritton 		rj->flags |= JF_FROM_RUNQ;
1974c86c0faSJamie Gritton 		requeue(rj, &ready);
1984c86c0faSJamie Gritton 	}
1992671ee73SJamie Gritton 	error = 0;
2002671ee73SJamie Gritton 	if (j->flags & JF_TIMEOUT) {
2012671ee73SJamie Gritton 		j->flags &= ~JF_TIMEOUT;
2023b40332cSJamie Gritton 		if (*j->comparam != IP_STOP_TIMEOUT) {
2032671ee73SJamie Gritton 			jail_warnx(j, "%s: timed out", j->comline);
2042671ee73SJamie Gritton 			failed(j);
2052671ee73SJamie Gritton 			error = -1;
2062671ee73SJamie Gritton 		} else if (verbose > 0)
2072671ee73SJamie Gritton 			jail_note(j, "timed out\n");
2082671ee73SJamie Gritton 	} else if (j->pstatus != 0) {
2092671ee73SJamie Gritton 		if (WIFSIGNALED(j->pstatus))
2102671ee73SJamie Gritton 			jail_warnx(j, "%s: exited on signal %d",
2112671ee73SJamie Gritton 			    j->comline, WTERMSIG(j->pstatus));
2122671ee73SJamie Gritton 		else
2132671ee73SJamie Gritton 			jail_warnx(j, "%s: failed", j->comline);
214aa02af54SJamie Gritton 		j->pstatus = 0;
2152671ee73SJamie Gritton 		failed(j);
2162671ee73SJamie Gritton 		error = -1;
2172671ee73SJamie Gritton 	}
2182671ee73SJamie Gritton 	free(j->comline);
219aa02af54SJamie Gritton 	j->comline = NULL;
2202671ee73SJamie Gritton 	return error;
2212671ee73SJamie Gritton }
2222671ee73SJamie Gritton 
2232671ee73SJamie Gritton /*
22460080230SJamie Gritton  * Check for finished processes or timeouts.
2252671ee73SJamie Gritton  */
2262671ee73SJamie Gritton struct cfjail *
2272671ee73SJamie Gritton next_proc(int nonblock)
2282671ee73SJamie Gritton {
2292671ee73SJamie Gritton 	struct kevent ke;
2302671ee73SJamie Gritton 	struct timespec ts;
2312671ee73SJamie Gritton 	struct timespec *tsp;
2322671ee73SJamie Gritton 	struct cfjail *j;
2332671ee73SJamie Gritton 
2342671ee73SJamie Gritton 	if (!TAILQ_EMPTY(&sleeping)) {
2352671ee73SJamie Gritton 	again:
2362671ee73SJamie Gritton 		tsp = NULL;
2372671ee73SJamie Gritton 		if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) {
2382671ee73SJamie Gritton 			clock_gettime(CLOCK_REALTIME, &ts);
2392671ee73SJamie Gritton 			ts.tv_sec = j->timeout.tv_sec - ts.tv_sec;
2402671ee73SJamie Gritton 			ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec;
2412671ee73SJamie Gritton 			if (ts.tv_nsec < 0) {
2422671ee73SJamie Gritton 				ts.tv_sec--;
2432671ee73SJamie Gritton 				ts.tv_nsec += 1000000000;
2442671ee73SJamie Gritton 			}
2452671ee73SJamie Gritton 			if (ts.tv_sec < 0 ||
2462671ee73SJamie Gritton 			    (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
2472671ee73SJamie Gritton 				j->flags |= JF_TIMEOUT;
2482671ee73SJamie Gritton 				clear_procs(j);
2492671ee73SJamie Gritton 				return j;
2502671ee73SJamie Gritton 			}
2512671ee73SJamie Gritton 			tsp = &ts;
2522671ee73SJamie Gritton 		}
2532671ee73SJamie Gritton 		if (nonblock) {
2542671ee73SJamie Gritton 			ts.tv_sec = 0;
2552671ee73SJamie Gritton 			ts.tv_nsec = 0;
2562671ee73SJamie Gritton 			tsp = &ts;
2572671ee73SJamie Gritton 		}
2582671ee73SJamie Gritton 		switch (kevent(kq, NULL, 0, &ke, 1, tsp)) {
2592671ee73SJamie Gritton 		case -1:
2602671ee73SJamie Gritton 			if (errno != EINTR)
2612671ee73SJamie Gritton 				err(1, "kevent");
2622671ee73SJamie Gritton 			goto again;
2632671ee73SJamie Gritton 		case 0:
2642671ee73SJamie Gritton 			if (!nonblock) {
2652671ee73SJamie Gritton 				j = TAILQ_FIRST(&sleeping);
2662671ee73SJamie Gritton 				j->flags |= JF_TIMEOUT;
2672671ee73SJamie Gritton 				clear_procs(j);
2682671ee73SJamie Gritton 				return j;
2692671ee73SJamie Gritton 			}
2702671ee73SJamie Gritton 			break;
2712671ee73SJamie Gritton 		case 1:
2722671ee73SJamie Gritton 			(void)waitpid(ke.ident, NULL, WNOHANG);
2732671ee73SJamie Gritton 			if ((j = find_proc(ke.ident))) {
2742671ee73SJamie Gritton 				j->pstatus = ke.data;
2752671ee73SJamie Gritton 				return j;
2762671ee73SJamie Gritton 			}
2772671ee73SJamie Gritton 			goto again;
2782671ee73SJamie Gritton 		}
2792671ee73SJamie Gritton 	}
2802671ee73SJamie Gritton 	return NULL;
2812671ee73SJamie Gritton }
2822671ee73SJamie Gritton 
2832671ee73SJamie Gritton /*
2844c86c0faSJamie Gritton  * Run a single command for a jail, possibly inside the jail.
2853b40332cSJamie Gritton  */
286a6486f60SJamie Gritton static int
2873b40332cSJamie Gritton run_command(struct cfjail *j)
2883b40332cSJamie Gritton {
2893b40332cSJamie Gritton 	const struct passwd *pwd;
2903b40332cSJamie Gritton 	const struct cfstring *comstring, *s;
2913b40332cSJamie Gritton 	login_cap_t *lcap;
292d031802bSJamie Gritton 	const char **argv;
293d031802bSJamie Gritton 	char *acs, *cs, *comcs, *devpath;
294e0dfe185SAlexander Leidinger 	const char *jidstr, *conslog, *fmt, *path, *ruleset, *term, *username;
2953b40332cSJamie Gritton 	enum intparam comparam;
296e0dfe185SAlexander Leidinger 	size_t comlen, ret;
2973b40332cSJamie Gritton 	pid_t pid;
298466df976SKyle Evans 	cpusetid_t setid;
2993b40332cSJamie Gritton 	int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout;
3005fb611c2SJamie Gritton #if defined(INET) || defined(INET6)
301b22b6abdSSteven Hartland 	char *addr, *extrap, *p, *val;
3025fb611c2SJamie Gritton #endif
3033b40332cSJamie Gritton 
3043b40332cSJamie Gritton 	static char *cleanenv;
3053b40332cSJamie Gritton 
3062b00f7baSJamie Gritton 	/* Perform some operations that aren't actually commands */
3072b00f7baSJamie Gritton 	comparam = *j->comparam;
3082b00f7baSJamie Gritton 	down = j->flags & (JF_STOP | JF_FAILED);
3092b00f7baSJamie Gritton 	switch (comparam) {
3102b00f7baSJamie Gritton 	case IP_STOP_TIMEOUT:
3112b00f7baSJamie Gritton 		return term_procs(j);
3122b00f7baSJamie Gritton 
3132b00f7baSJamie Gritton 	case IP__OP:
3142b00f7baSJamie Gritton 		if (down) {
3151ca35de4SJamie Gritton 			if (jail_remove(j->jid) < 0 && errno == EPERM) {
3161ca35de4SJamie Gritton 				jail_warnx(j, "jail_remove: %s",
3171ca35de4SJamie Gritton 					   strerror(errno));
3181ca35de4SJamie Gritton 				return -1;
3191ca35de4SJamie Gritton 			}
320eadec913SJamie Gritton 			if (verbose > 0 || (verbose == 0 && (j->flags & JF_STOP
3212b00f7baSJamie Gritton 			    ? note_remove : j->name != NULL)))
3222b00f7baSJamie Gritton 			    jail_note(j, "removed\n");
3232b00f7baSJamie Gritton 			j->jid = -1;
3242b00f7baSJamie Gritton 			if (j->flags & JF_STOP)
3252b00f7baSJamie Gritton 				dep_done(j, DF_LIGHT);
3262b00f7baSJamie Gritton 			else
3272b00f7baSJamie Gritton 				j->flags &= ~JF_PERSIST;
3282b00f7baSJamie Gritton 		} else {
32960080230SJamie Gritton 			if (create_jail(j) < 0)
3302b00f7baSJamie Gritton 				return -1;
331a6486f60SJamie Gritton 			if (iflag)
332a6486f60SJamie Gritton 				printf("%d\n", j->jid);
3332b00f7baSJamie Gritton 			if (verbose >= 0 && (j->name || verbose > 0))
3342b00f7baSJamie Gritton 				jail_note(j, "created\n");
3352b00f7baSJamie Gritton 			dep_done(j, DF_LIGHT);
3362b00f7baSJamie Gritton 		}
33760080230SJamie Gritton 		return 0;
338e16fb8fbSJamie Gritton 
339e16fb8fbSJamie Gritton 	default: ;
3402b00f7baSJamie Gritton 	}
3413b40332cSJamie Gritton 	/*
3423b40332cSJamie Gritton 	 * Collect exec arguments.  Internal commands for network and
3433b40332cSJamie Gritton 	 * mounting build their own argument lists.
3443b40332cSJamie Gritton 	 */
3453b40332cSJamie Gritton 	comstring = j->comstring;
3463b40332cSJamie Gritton 	bg = 0;
3473b40332cSJamie Gritton 	switch (comparam) {
3485fb611c2SJamie Gritton #ifdef INET
3493b40332cSJamie Gritton 	case IP__IP4_IFADDR:
350b22b6abdSSteven Hartland 		argc = 0;
351b22b6abdSSteven Hartland 		val = alloca(strlen(comstring->s) + 1);
352b22b6abdSSteven Hartland 		strcpy(val, comstring->s);
353b22b6abdSSteven Hartland 		cs = val;
354b22b6abdSSteven Hartland 		extrap = NULL;
355b22b6abdSSteven Hartland 		while ((p = strchr(cs, ' ')) != NULL && strlen(p) > 1) {
356b22b6abdSSteven Hartland 			if (extrap == NULL) {
357b22b6abdSSteven Hartland 				*p = '\0';
358b22b6abdSSteven Hartland 				extrap = p + 1;
359b22b6abdSSteven Hartland 			}
360b22b6abdSSteven Hartland 			cs = p + 1;
361b22b6abdSSteven Hartland 			argc++;
362b22b6abdSSteven Hartland 		}
363b22b6abdSSteven Hartland 
364b22b6abdSSteven Hartland 		argv = alloca((8 + argc) * sizeof(char *));
365d031802bSJamie Gritton 		argv[0] = _PATH_IFCONFIG;
366b22b6abdSSteven Hartland 		if ((cs = strchr(val, '|'))) {
367d031802bSJamie Gritton 			argv[1] = acs = alloca(cs - val + 1);
368d031802bSJamie Gritton 			strlcpy(acs, val, cs - val + 1);
3693b40332cSJamie Gritton 			addr = cs + 1;
3703b40332cSJamie Gritton 		} else {
371d031802bSJamie Gritton 			argv[1] = string_param(j->intparams[IP_INTERFACE]);
372b22b6abdSSteven Hartland 			addr = val;
3733b40332cSJamie Gritton 		}
374d031802bSJamie Gritton 		argv[2] = "inet";
3753b40332cSJamie Gritton 		if (!(cs = strchr(addr, '/'))) {
3763b40332cSJamie Gritton 			argv[3] = addr;
377d031802bSJamie Gritton 			argv[4] = "netmask";
378d031802bSJamie Gritton 			argv[5] = "255.255.255.255";
3793b40332cSJamie Gritton 			argc = 6;
3803b40332cSJamie Gritton 		} else if (strchr(cs + 1, '.')) {
381d031802bSJamie Gritton 			argv[3] = acs = alloca(cs - addr + 1);
382d031802bSJamie Gritton 			strlcpy(acs, addr, cs - addr + 1);
383d031802bSJamie Gritton 			argv[4] = "netmask";
384d031802bSJamie Gritton 			argv[5] = cs + 1;
3853b40332cSJamie Gritton 			argc = 6;
3863b40332cSJamie Gritton 		} else {
3873b40332cSJamie Gritton 			argv[3] = addr;
3883b40332cSJamie Gritton 			argc = 4;
3893b40332cSJamie Gritton 		}
390b22b6abdSSteven Hartland 
391bd24e861SEugene Grosbein 		if (!down && extrap != NULL) {
392d031802bSJamie Gritton 			for (cs = strtok(extrap, " "); cs;
393d031802bSJamie Gritton 			     cs = strtok(NULL, " ")) {
394b22b6abdSSteven Hartland 				size_t len = strlen(cs) + 1;
395d031802bSJamie Gritton 				argv[argc++] = acs = alloca(len);
396d031802bSJamie Gritton 				strlcpy(acs, cs, len);
397b22b6abdSSteven Hartland 			}
398b22b6abdSSteven Hartland 		}
399b22b6abdSSteven Hartland 
400d031802bSJamie Gritton 		argv[argc] = down ? "-alias" : "alias";
4013b40332cSJamie Gritton 		argv[argc + 1] = NULL;
4023b40332cSJamie Gritton 		break;
4035fb611c2SJamie Gritton #endif
4043b40332cSJamie Gritton 
4053b40332cSJamie Gritton #ifdef INET6
4063b40332cSJamie Gritton 	case IP__IP6_IFADDR:
407b22b6abdSSteven Hartland 		argc = 0;
408b22b6abdSSteven Hartland 		val = alloca(strlen(comstring->s) + 1);
409b22b6abdSSteven Hartland 		strcpy(val, comstring->s);
410b22b6abdSSteven Hartland 		cs = val;
411b22b6abdSSteven Hartland 		extrap = NULL;
412b22b6abdSSteven Hartland 		while ((p = strchr(cs, ' ')) != NULL && strlen(p) > 1) {
413b22b6abdSSteven Hartland 			if (extrap == NULL) {
414b22b6abdSSteven Hartland 				*p = '\0';
415b22b6abdSSteven Hartland 				extrap = p + 1;
416b22b6abdSSteven Hartland 			}
417b22b6abdSSteven Hartland 			cs = p + 1;
418b22b6abdSSteven Hartland 			argc++;
419b22b6abdSSteven Hartland 		}
420b22b6abdSSteven Hartland 
421b22b6abdSSteven Hartland 		argv = alloca((8 + argc) * sizeof(char *));
422d031802bSJamie Gritton 		argv[0] = _PATH_IFCONFIG;
423b22b6abdSSteven Hartland 		if ((cs = strchr(val, '|'))) {
424d031802bSJamie Gritton 			argv[1] = acs = alloca(cs - val + 1);
425d031802bSJamie Gritton 			strlcpy(acs, val, cs - val + 1);
4263b40332cSJamie Gritton 			addr = cs + 1;
4273b40332cSJamie Gritton 		} else {
428d031802bSJamie Gritton 			argv[1] = string_param(j->intparams[IP_INTERFACE]);
429b22b6abdSSteven Hartland 			addr = val;
4303b40332cSJamie Gritton 		}
431d031802bSJamie Gritton 		argv[2] = "inet6";
4323b40332cSJamie Gritton 		argv[3] = addr;
4333b40332cSJamie Gritton 		if (!(cs = strchr(addr, '/'))) {
434d031802bSJamie Gritton 			argv[4] = "prefixlen";
435d031802bSJamie Gritton 			argv[5] = "128";
4363b40332cSJamie Gritton 			argc = 6;
4373b40332cSJamie Gritton 		} else
4383b40332cSJamie Gritton 			argc = 4;
439b22b6abdSSteven Hartland 
440e8d7ae91SJamie Gritton 		if (!down && extrap != NULL) {
441d031802bSJamie Gritton 			for (cs = strtok(extrap, " "); cs;
442d031802bSJamie Gritton 			     cs = strtok(NULL, " ")) {
443b22b6abdSSteven Hartland 				size_t len = strlen(cs) + 1;
444d031802bSJamie Gritton 				argv[argc++] = acs = alloca(len);
445d031802bSJamie Gritton 				strlcpy(acs, cs, len);
446b22b6abdSSteven Hartland 			}
447b22b6abdSSteven Hartland 		}
448b22b6abdSSteven Hartland 
449d031802bSJamie Gritton 		argv[argc] = down ? "-alias" : "alias";
4503b40332cSJamie Gritton 		argv[argc + 1] = NULL;
4513b40332cSJamie Gritton 		break;
4523b40332cSJamie Gritton #endif
4533b40332cSJamie Gritton 
4543b40332cSJamie Gritton 	case IP_VNET_INTERFACE:
4553b40332cSJamie Gritton 		argv = alloca(5 * sizeof(char *));
456d031802bSJamie Gritton 		argv[0] = _PATH_IFCONFIG;
4573b40332cSJamie Gritton 		argv[1] = comstring->s;
458d031802bSJamie Gritton 		argv[2] = down ? "-vnet" : "vnet";
4593b40332cSJamie Gritton 		jidstr = string_param(j->intparams[KP_JID]);
460d031802bSJamie Gritton 		argv[3] = jidstr ? jidstr : string_param(j->intparams[KP_NAME]);
4613b40332cSJamie Gritton 		argv[4] = NULL;
4623b40332cSJamie Gritton 		break;
4633b40332cSJamie Gritton 
4643b40332cSJamie Gritton 	case IP_MOUNT:
4653b40332cSJamie Gritton 	case IP__MOUNT_FROM_FSTAB:
4663b40332cSJamie Gritton 		argv = alloca(8 * sizeof(char *));
4673b40332cSJamie Gritton 		comcs = alloca(comstring->len + 1);
4683b40332cSJamie Gritton 		strcpy(comcs, comstring->s);
4693b40332cSJamie Gritton 		argc = 0;
4703b40332cSJamie Gritton 		for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4;
471a99d8210SJamie Gritton 		     cs = strtok(NULL, " \t\f\v\r\n")) {
472a99d8210SJamie Gritton 			if (argc <= 1 && strunvis(cs, cs) < 0) {
473a99d8210SJamie Gritton 				jail_warnx(j, "%s: %s: fstab parse error",
474a99d8210SJamie Gritton 				    j->intparams[comparam]->name, comstring->s);
475a99d8210SJamie Gritton 				return -1;
476a99d8210SJamie Gritton 			}
4773b40332cSJamie Gritton 			argv[argc++] = cs;
478a99d8210SJamie Gritton 		}
4793b40332cSJamie Gritton 		if (argc == 0)
4803b40332cSJamie Gritton 			return 0;
4813b40332cSJamie Gritton 		if (argc < 3) {
4823b40332cSJamie Gritton 			jail_warnx(j, "%s: %s: missing information",
4833b40332cSJamie Gritton 			    j->intparams[comparam]->name, comstring->s);
4843b40332cSJamie Gritton 			return -1;
4853b40332cSJamie Gritton 		}
4863b40332cSJamie Gritton 		if (check_path(j, j->intparams[comparam]->name, argv[1], 0,
48760080230SJamie Gritton 		    down ? argv[2] : NULL) < 0)
4883b40332cSJamie Gritton 			return -1;
4893b40332cSJamie Gritton 		if (down) {
4903b40332cSJamie Gritton 			argv[4] = NULL;
4913b40332cSJamie Gritton 			argv[3] = argv[1];
492d031802bSJamie Gritton 			argv[0] = "/sbin/umount";
4933b40332cSJamie Gritton 		} else {
4943b40332cSJamie Gritton 			if (argc == 4) {
4953b40332cSJamie Gritton 				argv[7] = NULL;
4963b40332cSJamie Gritton 				argv[6] = argv[1];
4973b40332cSJamie Gritton 				argv[5] = argv[0];
4983b40332cSJamie Gritton 				argv[4] = argv[3];
499d031802bSJamie Gritton 				argv[3] = "-o";
5003b40332cSJamie Gritton 			} else {
5013b40332cSJamie Gritton 				argv[5] = NULL;
5023b40332cSJamie Gritton 				argv[4] = argv[1];
5033b40332cSJamie Gritton 				argv[3] = argv[0];
5043b40332cSJamie Gritton 			}
505d031802bSJamie Gritton 			argv[0] = _PATH_MOUNT;
5063b40332cSJamie Gritton 		}
507d031802bSJamie Gritton 		argv[1] = "-t";
5083b40332cSJamie Gritton 		break;
5093b40332cSJamie Gritton 
5103b40332cSJamie Gritton 	case IP_MOUNT_DEVFS:
5110c4d49e9SJamie Gritton 		argv = alloca(7 * sizeof(char *));
5123b40332cSJamie Gritton 		path = string_param(j->intparams[KP_PATH]);
5133b40332cSJamie Gritton 		if (path == NULL) {
514be659d72SDmitry Morozovsky 			jail_warnx(j, "mount.devfs: no jail root path defined");
5153b40332cSJamie Gritton 			return -1;
5163b40332cSJamie Gritton 		}
5173b40332cSJamie Gritton 		devpath = alloca(strlen(path) + 5);
5183b40332cSJamie Gritton 		sprintf(devpath, "%s/dev", path);
5193b40332cSJamie Gritton 		if (check_path(j, "mount.devfs", devpath, 0,
52060080230SJamie Gritton 		    down ? "devfs" : NULL) < 0)
5213b40332cSJamie Gritton 			return -1;
5223b40332cSJamie Gritton 		if (down) {
523d031802bSJamie Gritton 			argv[0] = "/sbin/umount";
5243b40332cSJamie Gritton 			argv[1] = devpath;
5253b40332cSJamie Gritton 			argv[2] = NULL;
5263b40332cSJamie Gritton 		} else {
527d031802bSJamie Gritton 			argv[0] = _PATH_MOUNT;
528d031802bSJamie Gritton 			argv[1] = "-t";
529d031802bSJamie Gritton 			argv[2] = "devfs";
5300c4d49e9SJamie Gritton 			ruleset = string_param(j->intparams[KP_DEVFS_RULESET]);
5310c4d49e9SJamie Gritton 			if (!ruleset)
5320c4d49e9SJamie Gritton 			    ruleset = "4";	/* devfsrules_jail */
533d031802bSJamie Gritton 			argv[3] = acs = alloca(11 + strlen(ruleset));
534d031802bSJamie Gritton 			sprintf(acs, "-oruleset=%s", ruleset);
535d031802bSJamie Gritton 			argv[4] = ".";
5360c4d49e9SJamie Gritton 			argv[5] = devpath;
5370c4d49e9SJamie Gritton 			argv[6] = NULL;
5383b40332cSJamie Gritton 		}
5393b40332cSJamie Gritton 		break;
5403b40332cSJamie Gritton 
541fbd868c9SHiroki Sato 	case IP_MOUNT_FDESCFS:
542fbd868c9SHiroki Sato 		argv = alloca(7 * sizeof(char *));
543fbd868c9SHiroki Sato 		path = string_param(j->intparams[KP_PATH]);
544fbd868c9SHiroki Sato 		if (path == NULL) {
545be659d72SDmitry Morozovsky 			jail_warnx(j, "mount.fdescfs: no jail root path defined");
546fbd868c9SHiroki Sato 			return -1;
547fbd868c9SHiroki Sato 		}
548fbd868c9SHiroki Sato 		devpath = alloca(strlen(path) + 8);
549fbd868c9SHiroki Sato 		sprintf(devpath, "%s/dev/fd", path);
550fbd868c9SHiroki Sato 		if (check_path(j, "mount.fdescfs", devpath, 0,
551fbd868c9SHiroki Sato 		    down ? "fdescfs" : NULL) < 0)
552fbd868c9SHiroki Sato 			return -1;
553fbd868c9SHiroki Sato 		if (down) {
554d031802bSJamie Gritton 			argv[0] = "/sbin/umount";
555fbd868c9SHiroki Sato 			argv[1] = devpath;
556fbd868c9SHiroki Sato 			argv[2] = NULL;
557fbd868c9SHiroki Sato 		} else {
558d031802bSJamie Gritton 			argv[0] = _PATH_MOUNT;
559d031802bSJamie Gritton 			argv[1] = "-t";
560d031802bSJamie Gritton 			argv[2] = "fdescfs";
561d031802bSJamie Gritton 			argv[3] = ".";
562fbd868c9SHiroki Sato 			argv[4] = devpath;
563fbd868c9SHiroki Sato 			argv[5] = NULL;
564fbd868c9SHiroki Sato 		}
565fbd868c9SHiroki Sato 		break;
566fbd868c9SHiroki Sato 
56707a7869fSJamie Gritton 	case IP_MOUNT_PROCFS:
56807a7869fSJamie Gritton 		argv = alloca(7 * sizeof(char *));
56907a7869fSJamie Gritton 		path = string_param(j->intparams[KP_PATH]);
57007a7869fSJamie Gritton 		if (path == NULL) {
571be659d72SDmitry Morozovsky 			jail_warnx(j, "mount.procfs: no jail root path defined");
57207a7869fSJamie Gritton 			return -1;
57307a7869fSJamie Gritton 		}
57407a7869fSJamie Gritton 		devpath = alloca(strlen(path) + 6);
57507a7869fSJamie Gritton 		sprintf(devpath, "%s/proc", path);
57607a7869fSJamie Gritton 		if (check_path(j, "mount.procfs", devpath, 0,
57707a7869fSJamie Gritton 		    down ? "procfs" : NULL) < 0)
57807a7869fSJamie Gritton 			return -1;
57907a7869fSJamie Gritton 		if (down) {
58007a7869fSJamie Gritton 			argv[0] = "/sbin/umount";
58107a7869fSJamie Gritton 			argv[1] = devpath;
58207a7869fSJamie Gritton 			argv[2] = NULL;
58307a7869fSJamie Gritton 		} else {
58407a7869fSJamie Gritton 			argv[0] = _PATH_MOUNT;
58507a7869fSJamie Gritton 			argv[1] = "-t";
58607a7869fSJamie Gritton 			argv[2] = "procfs";
58707a7869fSJamie Gritton 			argv[3] = ".";
58807a7869fSJamie Gritton 			argv[4] = devpath;
58907a7869fSJamie Gritton 			argv[5] = NULL;
59007a7869fSJamie Gritton 		}
59107a7869fSJamie Gritton 		break;
59207a7869fSJamie Gritton 
593e0dfe185SAlexander Leidinger 	case IP_ZFS_DATASET:
594e0dfe185SAlexander Leidinger 		argv = alloca(4 * sizeof(char *));
595e0dfe185SAlexander Leidinger 		jidstr = string_param(j->intparams[KP_JID]) ?
596e0dfe185SAlexander Leidinger 		    string_param(j->intparams[KP_JID]) :
597e0dfe185SAlexander Leidinger 		    string_param(j->intparams[KP_NAME]);
598e0dfe185SAlexander Leidinger 		fmt = "if [ $(/sbin/zfs get -H -o value jailed %s) = on ]; then /sbin/zfs jail %s %s || echo error, attaching %s to jail %s failed; else echo error, you need to set jailed=on for dataset %s; fi";
599e0dfe185SAlexander Leidinger 		comlen = strlen(fmt)
600e0dfe185SAlexander Leidinger 		    + 2 * strlen(jidstr)
601e0dfe185SAlexander Leidinger 		    + 4 * comstring->len
602e0dfe185SAlexander Leidinger 		    - 6 * 2	/* 6 * "%s" */
603e0dfe185SAlexander Leidinger 		    + 1;
604e0dfe185SAlexander Leidinger 		comcs = alloca(comlen);
605e0dfe185SAlexander Leidinger 		ret = snprintf(comcs, comlen, fmt, comstring->s,
606e0dfe185SAlexander Leidinger 		    jidstr, comstring->s, comstring->s, jidstr,
607e0dfe185SAlexander Leidinger 		    comstring->s);
608e0dfe185SAlexander Leidinger 		if (ret >= comlen) {
609e0dfe185SAlexander Leidinger 			jail_warnx(j, "internal error in ZFS dataset handling");
610e0dfe185SAlexander Leidinger 			exit(1);
611e0dfe185SAlexander Leidinger 		}
612e0dfe185SAlexander Leidinger 		argv[0] = _PATH_BSHELL;
613e0dfe185SAlexander Leidinger 		argv[1] = "-c";
614e0dfe185SAlexander Leidinger 		argv[2] = comcs;
615e0dfe185SAlexander Leidinger 		argv[3] = NULL;
616e0dfe185SAlexander Leidinger 		break;
617e0dfe185SAlexander Leidinger 
6183b40332cSJamie Gritton 	case IP_COMMAND:
6193b40332cSJamie Gritton 		if (j->name != NULL)
6203b40332cSJamie Gritton 			goto default_command;
6213b40332cSJamie Gritton 		argc = 0;
6223b40332cSJamie Gritton 		TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
6233b40332cSJamie Gritton 			argc++;
6243b40332cSJamie Gritton 		argv = alloca((argc + 1) * sizeof(char *));
6253b40332cSJamie Gritton 		argc = 0;
6263b40332cSJamie Gritton 		TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
6273b40332cSJamie Gritton 			argv[argc++] = s->s;
6283b40332cSJamie Gritton 		argv[argc] = NULL;
629c6eff841SJamie Gritton 		j->comstring = &dummystring;
6303b40332cSJamie Gritton 		break;
6313b40332cSJamie Gritton 
6323b40332cSJamie Gritton 	default:
6333b40332cSJamie Gritton 	default_command:
6343b40332cSJamie Gritton 		if ((cs = strpbrk(comstring->s, "!\"$&'()*;<>?[\\]`{|}~")) &&
6353b40332cSJamie Gritton 		    !(cs[0] == '&' && cs[1] == '\0')) {
6363b40332cSJamie Gritton 			argv = alloca(4 * sizeof(char *));
637d031802bSJamie Gritton 			argv[0] = _PATH_BSHELL;
638d031802bSJamie Gritton 			argv[1] = "-c";
6393b40332cSJamie Gritton 			argv[2] = comstring->s;
6403b40332cSJamie Gritton 			argv[3] = NULL;
6413b40332cSJamie Gritton 		} else {
6423b40332cSJamie Gritton 			if (cs) {
6433b40332cSJamie Gritton 				*cs = 0;
6443b40332cSJamie Gritton 				bg = 1;
6453b40332cSJamie Gritton 			}
6463b40332cSJamie Gritton 			comcs = alloca(comstring->len + 1);
6473b40332cSJamie Gritton 			strcpy(comcs, comstring->s);
6483b40332cSJamie Gritton 			argc = 0;
6493b40332cSJamie Gritton 			for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
6503b40332cSJamie Gritton 			     cs = strtok(NULL, " \t\f\v\r\n"))
6513b40332cSJamie Gritton 				argc++;
6523b40332cSJamie Gritton 			argv = alloca((argc + 1) * sizeof(char *));
6533b40332cSJamie Gritton 			strcpy(comcs, comstring->s);
6543b40332cSJamie Gritton 			argc = 0;
6553b40332cSJamie Gritton 			for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
6563b40332cSJamie Gritton 			     cs = strtok(NULL, " \t\f\v\r\n"))
6573b40332cSJamie Gritton 				argv[argc++] = cs;
6583b40332cSJamie Gritton 			argv[argc] = NULL;
6593b40332cSJamie Gritton 		}
6603b40332cSJamie Gritton 	}
6613b40332cSJamie Gritton 	if (argv[0] == NULL)
6623b40332cSJamie Gritton 		return 0;
6633b40332cSJamie Gritton 
6643b40332cSJamie Gritton 	if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) &&
6653b40332cSJamie Gritton 	    timeout != 0) {
6663b40332cSJamie Gritton 		clock_gettime(CLOCK_REALTIME, &j->timeout);
6673b40332cSJamie Gritton 		j->timeout.tv_sec += timeout;
6683b40332cSJamie Gritton 	} else
6693b40332cSJamie Gritton 		j->timeout.tv_sec = 0;
6703b40332cSJamie Gritton 
6713b40332cSJamie Gritton 	injail = comparam == IP_EXEC_START || comparam == IP_COMMAND ||
6723b40332cSJamie Gritton 	    comparam == IP_EXEC_STOP;
673466df976SKyle Evans 	if (injail)
674466df976SKyle Evans 		setid = root_cpuset_id();
675466df976SKyle Evans 	else
676466df976SKyle Evans 		setid = CPUSET_INVALID;
6773b40332cSJamie Gritton 	clean = bool_param(j->intparams[IP_EXEC_CLEAN]);
6783b40332cSJamie Gritton 	username = string_param(j->intparams[injail
6793b40332cSJamie Gritton 	    ? IP_EXEC_JAIL_USER : IP_EXEC_SYSTEM_USER]);
6803b40332cSJamie Gritton 	sjuser = bool_param(j->intparams[IP_EXEC_SYSTEM_JAIL_USER]);
6813b40332cSJamie Gritton 
6823b40332cSJamie Gritton 	consfd = 0;
6833b40332cSJamie Gritton 	if (injail &&
6843b40332cSJamie Gritton 	    (conslog = string_param(j->intparams[IP_EXEC_CONSOLELOG]))) {
68560080230SJamie Gritton 		if (check_path(j, "exec.consolelog", conslog, 1, NULL) < 0)
6863b40332cSJamie Gritton 			return -1;
6873b40332cSJamie Gritton 		consfd =
6883b40332cSJamie Gritton 		    open(conslog, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE);
6893b40332cSJamie Gritton 		if (consfd < 0) {
6903b40332cSJamie Gritton 			jail_warnx(j, "open %s: %s", conslog, strerror(errno));
6913b40332cSJamie Gritton 			return -1;
6923b40332cSJamie Gritton 		}
6933b40332cSJamie Gritton 	}
6943b40332cSJamie Gritton 
6953b40332cSJamie Gritton 	comlen = 0;
6963b40332cSJamie Gritton 	for (i = 0; argv[i]; i++)
6973b40332cSJamie Gritton 		comlen += strlen(argv[i]) + 1;
6983b40332cSJamie Gritton 	j->comline = cs = emalloc(comlen);
6993b40332cSJamie Gritton 	for (i = 0; argv[i]; i++) {
7003b40332cSJamie Gritton 		strcpy(cs, argv[i]);
7013b40332cSJamie Gritton 		if (argv[i + 1]) {
7023b40332cSJamie Gritton 			cs += strlen(argv[i]) + 1;
7033b40332cSJamie Gritton 			cs[-1] = ' ';
7043b40332cSJamie Gritton 		}
7053b40332cSJamie Gritton 	}
7063b40332cSJamie Gritton 	if (verbose > 0)
7073b40332cSJamie Gritton 		jail_note(j, "run command%s%s%s: %s\n",
7083b40332cSJamie Gritton 		    injail ? " in jail" : "", username ? " as " : "",
7093b40332cSJamie Gritton 		    username ? username : "", j->comline);
7103b40332cSJamie Gritton 
7113b40332cSJamie Gritton 	pid = fork();
7123b40332cSJamie Gritton 	if (pid < 0)
7133b40332cSJamie Gritton 		err(1, "fork");
7143b40332cSJamie Gritton 	if (pid > 0) {
715e5935495SJamie Gritton 		if (bg || !add_proc(j, pid)) {
7163b40332cSJamie Gritton 			free(j->comline);
7173b40332cSJamie Gritton 			j->comline = NULL;
71860080230SJamie Gritton 			return 0;
7193b40332cSJamie Gritton 		} else {
7203b40332cSJamie Gritton 			paralimit--;
7213b40332cSJamie Gritton 			return 1;
7223b40332cSJamie Gritton 		}
72360080230SJamie Gritton 	}
7243b40332cSJamie Gritton 	if (bg)
7253b40332cSJamie Gritton 		setsid();
7263b40332cSJamie Gritton 
7273b40332cSJamie Gritton 	/* Set up the environment and run the command */
7283b40332cSJamie Gritton 	pwd = NULL;
7293b40332cSJamie Gritton 	lcap = NULL;
7303b40332cSJamie Gritton 	if ((clean || username) && injail && sjuser &&
7313b40332cSJamie Gritton 	    get_user_info(j, username, &pwd, &lcap) < 0)
7323b40332cSJamie Gritton 		exit(1);
7333b40332cSJamie Gritton 	if (injail) {
7343b40332cSJamie Gritton 		/* jail_attach won't chdir along with its chroot. */
7353b40332cSJamie Gritton 		path = string_param(j->intparams[KP_PATH]);
7363b40332cSJamie Gritton 		if (path && chdir(path) < 0) {
7373b40332cSJamie Gritton 			jail_warnx(j, "chdir %s: %s", path, strerror(errno));
7383b40332cSJamie Gritton 			exit(1);
7393b40332cSJamie Gritton 		}
7403b40332cSJamie Gritton 		if (int_param(j->intparams[IP_EXEC_FIB], &fib) &&
7413b40332cSJamie Gritton 		    setfib(fib) < 0) {
7423b40332cSJamie Gritton 			jail_warnx(j, "setfib: %s", strerror(errno));
7433b40332cSJamie Gritton 			exit(1);
7443b40332cSJamie Gritton 		}
745466df976SKyle Evans 
746466df976SKyle Evans 		/*
747466df976SKyle Evans 		 * We wouldn't have specialized our affinity, so just setid to
748466df976SKyle Evans 		 * root.  We do this prior to attaching to avoid the kernel
749466df976SKyle Evans 		 * having to create a transient cpuset that we'll promptly
750466df976SKyle Evans 		 * free up with a reset to the jail's cpuset.
751466df976SKyle Evans 		 *
752466df976SKyle Evans 		 * This is just a best-effort to use as wide of mask as
753466df976SKyle Evans 		 * possible.
754466df976SKyle Evans 		 */
755466df976SKyle Evans 		if (setid != CPUSET_INVALID)
756466df976SKyle Evans 			(void)cpuset_setid(CPU_WHICH_PID, -1, setid);
757466df976SKyle Evans 
7583b40332cSJamie Gritton 		if (jail_attach(j->jid) < 0) {
7593b40332cSJamie Gritton 			jail_warnx(j, "jail_attach: %s", strerror(errno));
7603b40332cSJamie Gritton 			exit(1);
7613b40332cSJamie Gritton 		}
7623b40332cSJamie Gritton 	}
7633b40332cSJamie Gritton 	if (clean || username) {
7643b40332cSJamie Gritton 		if (!(injail && sjuser) &&
7653b40332cSJamie Gritton 		    get_user_info(j, username, &pwd, &lcap) < 0)
7663b40332cSJamie Gritton 			exit(1);
7673b40332cSJamie Gritton 		if (clean) {
7683b40332cSJamie Gritton 			term = getenv("TERM");
7693b40332cSJamie Gritton 			environ = &cleanenv;
7703b40332cSJamie Gritton 			setenv("PATH", "/bin:/usr/bin", 0);
7718632fa3eSJamie Gritton 			if (term != NULL)
7723b40332cSJamie Gritton 				setenv("TERM", term, 1);
7733b40332cSJamie Gritton 		}
774eb28afe4SJamie Gritton 		if (setgid(pwd->pw_gid) < 0) {
775eb28afe4SJamie Gritton 			jail_warnx(j, "setgid %d: %s", pwd->pw_gid,
776eb28afe4SJamie Gritton 			    strerror(errno));
777eb28afe4SJamie Gritton 			exit(1);
778eb28afe4SJamie Gritton 		}
7793b40332cSJamie Gritton 		if (setusercontext(lcap, pwd, pwd->pw_uid, username
7803b40332cSJamie Gritton 		    ? LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN
7813b40332cSJamie Gritton 		    : LOGIN_SETPATH | LOGIN_SETENV) < 0) {
7823b40332cSJamie Gritton 			jail_warnx(j, "setusercontext %s: %s", pwd->pw_name,
7833b40332cSJamie Gritton 			    strerror(errno));
7843b40332cSJamie Gritton 			exit(1);
7853b40332cSJamie Gritton 		}
7863b40332cSJamie Gritton 		login_close(lcap);
7873b40332cSJamie Gritton 		setenv("USER", pwd->pw_name, 1);
7883b40332cSJamie Gritton 		setenv("HOME", pwd->pw_dir, 1);
7893b40332cSJamie Gritton 		setenv("SHELL",
7903b40332cSJamie Gritton 		    *pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL, 1);
791*5cf70549SJamie Gritton 		if (clean && username && chdir(pwd->pw_dir) < 0) {
7923b40332cSJamie Gritton 			jail_warnx(j, "chdir %s: %s",
7933b40332cSJamie Gritton 			    pwd->pw_dir, strerror(errno));
7943b40332cSJamie Gritton 			exit(1);
7953b40332cSJamie Gritton 		}
7963b40332cSJamie Gritton 		endpwent();
7973b40332cSJamie Gritton 	}
7983b40332cSJamie Gritton 
7993b40332cSJamie Gritton 	if (consfd != 0 && (dup2(consfd, 1) < 0 || dup2(consfd, 2) < 0)) {
8003b40332cSJamie Gritton 		jail_warnx(j, "exec.consolelog: %s", strerror(errno));
8013b40332cSJamie Gritton 		exit(1);
8023b40332cSJamie Gritton 	}
8033b40332cSJamie Gritton 	closefrom(3);
804d031802bSJamie Gritton 	execvp(argv[0], __DECONST(char *const*, argv));
8053b40332cSJamie Gritton 	jail_warnx(j, "exec %s: %s", argv[0], strerror(errno));
8063b40332cSJamie Gritton 	exit(1);
8073b40332cSJamie Gritton }
8083b40332cSJamie Gritton 
8093b40332cSJamie Gritton /*
810aa02af54SJamie Gritton  * Add a process to the hash, tied to a jail.
811aa02af54SJamie Gritton  */
812e5935495SJamie Gritton static int
813aa02af54SJamie Gritton add_proc(struct cfjail *j, pid_t pid)
814aa02af54SJamie Gritton {
815aa02af54SJamie Gritton 	struct kevent ke;
816aa02af54SJamie Gritton 	struct cfjail *tj;
817aa02af54SJamie Gritton 	struct phash *ph;
818aa02af54SJamie Gritton 
819aa02af54SJamie Gritton 	if (!kq && (kq = kqueue()) < 0)
820aa02af54SJamie Gritton 		err(1, "kqueue");
821aa02af54SJamie Gritton 	EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
822e5935495SJamie Gritton 	if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) {
823e5935495SJamie Gritton 		if (errno == ESRCH)
824e5935495SJamie Gritton 			return 0;
825aa02af54SJamie Gritton 		err(1, "kevent");
826e5935495SJamie Gritton 	}
827aa02af54SJamie Gritton 	ph = emalloc(sizeof(struct phash));
828aa02af54SJamie Gritton 	ph->j = j;
829aa02af54SJamie Gritton 	ph->pid = pid;
830aa02af54SJamie Gritton 	LIST_INSERT_HEAD(&phash[pid % PHASH_SIZE], ph, le);
831aa02af54SJamie Gritton 	j->nprocs++;
832aa02af54SJamie Gritton 	j->flags |= JF_SLEEPQ;
833aa02af54SJamie Gritton 	if (j->timeout.tv_sec == 0)
834aa02af54SJamie Gritton 		requeue(j, &sleeping);
835aa02af54SJamie Gritton 	else {
836463a577bSEitan Adler 		/* File the jail in the sleep queue according to its timeout. */
837aa02af54SJamie Gritton 		TAILQ_REMOVE(j->queue, j, tq);
838aa02af54SJamie Gritton 		TAILQ_FOREACH(tj, &sleeping, tq) {
839aa02af54SJamie Gritton 			if (!tj->timeout.tv_sec ||
840aa02af54SJamie Gritton 			    j->timeout.tv_sec < tj->timeout.tv_sec ||
841aa02af54SJamie Gritton 			    (j->timeout.tv_sec == tj->timeout.tv_sec &&
842aa02af54SJamie Gritton 			    j->timeout.tv_nsec <= tj->timeout.tv_nsec)) {
843aa02af54SJamie Gritton 				TAILQ_INSERT_BEFORE(tj, j, tq);
844aa02af54SJamie Gritton 				break;
845aa02af54SJamie Gritton 			}
846aa02af54SJamie Gritton 		}
847aa02af54SJamie Gritton 		if (tj == NULL)
848aa02af54SJamie Gritton 			TAILQ_INSERT_TAIL(&sleeping, j, tq);
849aa02af54SJamie Gritton 		j->queue = &sleeping;
850aa02af54SJamie Gritton 	}
851e5935495SJamie Gritton 	return 1;
852aa02af54SJamie Gritton }
853aa02af54SJamie Gritton 
854aa02af54SJamie Gritton /*
855aa02af54SJamie Gritton  * Remove any processes from the hash that correspond to a jail.
856aa02af54SJamie Gritton  */
857aa02af54SJamie Gritton static void
858aa02af54SJamie Gritton clear_procs(struct cfjail *j)
859aa02af54SJamie Gritton {
860aa02af54SJamie Gritton 	struct kevent ke;
861aa02af54SJamie Gritton 	struct phash *ph, *tph;
862aa02af54SJamie Gritton 	int i;
863aa02af54SJamie Gritton 
864aa02af54SJamie Gritton 	j->nprocs = 0;
865aa02af54SJamie Gritton 	for (i = 0; i < PHASH_SIZE; i++)
866aa02af54SJamie Gritton 		LIST_FOREACH_SAFE(ph, &phash[i], le, tph)
867aa02af54SJamie Gritton 			if (ph->j == j) {
868aa02af54SJamie Gritton 				EV_SET(&ke, ph->pid, EVFILT_PROC, EV_DELETE,
869aa02af54SJamie Gritton 				    NOTE_EXIT, 0, NULL);
870aa02af54SJamie Gritton 				(void)kevent(kq, &ke, 1, NULL, 0, NULL);
871aa02af54SJamie Gritton 				LIST_REMOVE(ph, le);
872aa02af54SJamie Gritton 				free(ph);
873aa02af54SJamie Gritton 			}
874aa02af54SJamie Gritton }
875aa02af54SJamie Gritton 
876aa02af54SJamie Gritton /*
877aa02af54SJamie Gritton  * Find the jail that corresponds to an exited process.
878aa02af54SJamie Gritton  */
879aa02af54SJamie Gritton static struct cfjail *
880aa02af54SJamie Gritton find_proc(pid_t pid)
881aa02af54SJamie Gritton {
882aa02af54SJamie Gritton 	struct cfjail *j;
883aa02af54SJamie Gritton 	struct phash *ph;
884aa02af54SJamie Gritton 
885aa02af54SJamie Gritton 	LIST_FOREACH(ph, &phash[pid % PHASH_SIZE], le)
886aa02af54SJamie Gritton 		if (ph->pid == pid) {
887aa02af54SJamie Gritton 			j = ph->j;
888aa02af54SJamie Gritton 			LIST_REMOVE(ph, le);
889aa02af54SJamie Gritton 			free(ph);
890aa02af54SJamie Gritton 			return --j->nprocs ? NULL : j;
891aa02af54SJamie Gritton 		}
892aa02af54SJamie Gritton 	return NULL;
893aa02af54SJamie Gritton }
894aa02af54SJamie Gritton 
895aa02af54SJamie Gritton /*
8962671ee73SJamie Gritton  * Send SIGTERM to all processes in a jail and wait for them to die.
8972671ee73SJamie Gritton  */
898aa02af54SJamie Gritton static int
8992671ee73SJamie Gritton term_procs(struct cfjail *j)
9002671ee73SJamie Gritton {
9012671ee73SJamie Gritton 	struct kinfo_proc *ki;
9022671ee73SJamie Gritton 	int i, noted, pcnt, timeout;
9032671ee73SJamie Gritton 
9042671ee73SJamie Gritton 	static kvm_t *kd;
9052671ee73SJamie Gritton 
9062671ee73SJamie Gritton 	if (!int_param(j->intparams[IP_STOP_TIMEOUT], &timeout))
9072671ee73SJamie Gritton 		timeout = DEFAULT_STOP_TIMEOUT;
9082671ee73SJamie Gritton 	else if (timeout == 0)
9092671ee73SJamie Gritton 		return 0;
9102671ee73SJamie Gritton 
9112671ee73SJamie Gritton 	if (kd == NULL) {
9121ca35de4SJamie Gritton 		kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
9132671ee73SJamie Gritton 		if (kd == NULL)
9141ca35de4SJamie Gritton 			return 0;
9152671ee73SJamie Gritton 	}
9162671ee73SJamie Gritton 
9172671ee73SJamie Gritton 	ki = kvm_getprocs(kd, KERN_PROC_PROC, 0, &pcnt);
9182671ee73SJamie Gritton 	if (ki == NULL)
9191ca35de4SJamie Gritton 		return 0;
9202671ee73SJamie Gritton 	noted = 0;
9212671ee73SJamie Gritton 	for (i = 0; i < pcnt; i++)
9222671ee73SJamie Gritton 		if (ki[i].ki_jid == j->jid &&
9232671ee73SJamie Gritton 		    kill(ki[i].ki_pid, SIGTERM) == 0) {
924e5935495SJamie Gritton 			(void)add_proc(j, ki[i].ki_pid);
9252671ee73SJamie Gritton 			if (verbose > 0) {
9262671ee73SJamie Gritton 				if (!noted) {
9272671ee73SJamie Gritton 					noted = 1;
9282671ee73SJamie Gritton 					jail_note(j, "sent SIGTERM to:");
9292671ee73SJamie Gritton 				}
9302671ee73SJamie Gritton 				printf(" %d", ki[i].ki_pid);
9312671ee73SJamie Gritton 			}
9322671ee73SJamie Gritton 		}
9332671ee73SJamie Gritton 	if (noted)
9342671ee73SJamie Gritton 		printf("\n");
9352671ee73SJamie Gritton 	if (j->nprocs > 0) {
9362671ee73SJamie Gritton 		clock_gettime(CLOCK_REALTIME, &j->timeout);
9372671ee73SJamie Gritton 		j->timeout.tv_sec += timeout;
9382671ee73SJamie Gritton 		return 1;
9392671ee73SJamie Gritton 	}
9402671ee73SJamie Gritton 	return 0;
9412671ee73SJamie Gritton }
9422671ee73SJamie Gritton 
9432671ee73SJamie Gritton /*
9442671ee73SJamie Gritton  * Look up a user in the passwd and login.conf files.
9452671ee73SJamie Gritton  */
9462671ee73SJamie Gritton static int
9472671ee73SJamie Gritton get_user_info(struct cfjail *j, const char *username,
9482671ee73SJamie Gritton     const struct passwd **pwdp, login_cap_t **lcapp)
9492671ee73SJamie Gritton {
9502671ee73SJamie Gritton 	const struct passwd *pwd;
9512671ee73SJamie Gritton 
952fcc43d06SJamie Gritton 	errno = 0;
9532671ee73SJamie Gritton 	*pwdp = pwd = username ? getpwnam(username) : getpwuid(getuid());
9542671ee73SJamie Gritton 	if (pwd == NULL) {
9552671ee73SJamie Gritton 		if (errno)
9562671ee73SJamie Gritton 			jail_warnx(j, "getpwnam%s%s: %s", username ? " " : "",
9572671ee73SJamie Gritton 			    username ? username : "", strerror(errno));
9582671ee73SJamie Gritton 		else if (username)
9592671ee73SJamie Gritton 			jail_warnx(j, "%s: no such user", username);
9602671ee73SJamie Gritton 		else
9612671ee73SJamie Gritton 			jail_warnx(j, "unknown uid %d", getuid());
9622671ee73SJamie Gritton 		return -1;
9632671ee73SJamie Gritton 	}
9642671ee73SJamie Gritton 	*lcapp = login_getpwclass(pwd);
9652671ee73SJamie Gritton 	if (*lcapp == NULL) {
9662671ee73SJamie Gritton 		jail_warnx(j, "getpwclass %s: %s", pwd->pw_name,
9672671ee73SJamie Gritton 		    strerror(errno));
9682671ee73SJamie Gritton 		return -1;
9692671ee73SJamie Gritton 	}
9702671ee73SJamie Gritton 	/* Set the groups while the group file is still available */
9712671ee73SJamie Gritton 	if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) {
9722671ee73SJamie Gritton 		jail_warnx(j, "initgroups %s: %s", pwd->pw_name,
9732671ee73SJamie Gritton 		    strerror(errno));
9742671ee73SJamie Gritton 		return -1;
9752671ee73SJamie Gritton 	}
9762671ee73SJamie Gritton 	return 0;
9772671ee73SJamie Gritton }
9788ebbf0e2SJamie Gritton 
9798ebbf0e2SJamie Gritton /*
9808ebbf0e2SJamie Gritton  * Make sure a mount or consolelog path is a valid absolute pathname
9818ebbf0e2SJamie Gritton  * with no symlinks.
9828ebbf0e2SJamie Gritton  */
9838ebbf0e2SJamie Gritton static int
9845264032fSJamie Gritton check_path(struct cfjail *j, const char *pname, const char *path, int isfile,
9855264032fSJamie Gritton     const char *umount_type)
9868ebbf0e2SJamie Gritton {
9875264032fSJamie Gritton 	struct stat st, mpst;
9885264032fSJamie Gritton 	struct statfs stfs;
9898ebbf0e2SJamie Gritton 	char *tpath, *p;
9908ebbf0e2SJamie Gritton 	const char *jailpath;
9918ebbf0e2SJamie Gritton 	size_t jplen;
9928ebbf0e2SJamie Gritton 
9938ebbf0e2SJamie Gritton 	if (path[0] != '/') {
9948ebbf0e2SJamie Gritton 		jail_warnx(j, "%s: %s: not an absolute pathname",
9958ebbf0e2SJamie Gritton 		    pname, path);
9968ebbf0e2SJamie Gritton 		return -1;
9978ebbf0e2SJamie Gritton 	}
9988ebbf0e2SJamie Gritton 	/*
9998ebbf0e2SJamie Gritton 	 * Only check for symlinks in components below the jail's path,
10008ebbf0e2SJamie Gritton 	 * since that's where the security risk lies.
10018ebbf0e2SJamie Gritton 	 */
10028ebbf0e2SJamie Gritton 	jailpath = string_param(j->intparams[KP_PATH]);
10038ebbf0e2SJamie Gritton 	if (jailpath == NULL)
10048ebbf0e2SJamie Gritton 		jailpath = "";
10058ebbf0e2SJamie Gritton 	jplen = strlen(jailpath);
10065264032fSJamie Gritton 	if (!strncmp(path, jailpath, jplen) && path[jplen] == '/') {
10078ebbf0e2SJamie Gritton 		tpath = alloca(strlen(path) + 1);
10088ebbf0e2SJamie Gritton 		strcpy(tpath, path);
10098ebbf0e2SJamie Gritton 		for (p = tpath + jplen; p != NULL; ) {
10108ebbf0e2SJamie Gritton 			p = strchr(p + 1, '/');
10118ebbf0e2SJamie Gritton 			if (p)
10128ebbf0e2SJamie Gritton 				*p = '\0';
10138ebbf0e2SJamie Gritton 			if (lstat(tpath, &st) < 0) {
10148ebbf0e2SJamie Gritton 				if (errno == ENOENT && isfile && !p)
10158ebbf0e2SJamie Gritton 					break;
10168ebbf0e2SJamie Gritton 				jail_warnx(j, "%s: %s: %s", pname, tpath,
10178ebbf0e2SJamie Gritton 				    strerror(errno));
10188ebbf0e2SJamie Gritton 				return -1;
10198ebbf0e2SJamie Gritton 			}
10208ebbf0e2SJamie Gritton 			if (S_ISLNK(st.st_mode)) {
10218ebbf0e2SJamie Gritton 				jail_warnx(j, "%s: %s is a symbolic link",
10228ebbf0e2SJamie Gritton 				    pname, tpath);
10238ebbf0e2SJamie Gritton 				return -1;
10248ebbf0e2SJamie Gritton 			}
10258ebbf0e2SJamie Gritton 			if (p)
10268ebbf0e2SJamie Gritton 				*p = '/';
10278ebbf0e2SJamie Gritton 		}
10285264032fSJamie Gritton 	}
10295264032fSJamie Gritton 	if (umount_type != NULL) {
10305264032fSJamie Gritton 		if (stat(path, &st) < 0 || statfs(path, &stfs) < 0) {
10315264032fSJamie Gritton 			jail_warnx(j, "%s: %s: %s", pname, path,
10325264032fSJamie Gritton 			    strerror(errno));
10335264032fSJamie Gritton 			return -1;
10345264032fSJamie Gritton 		}
10355264032fSJamie Gritton 		if (stat(stfs.f_mntonname, &mpst) < 0) {
10365264032fSJamie Gritton 			jail_warnx(j, "%s: %s: %s", pname, stfs.f_mntonname,
10375264032fSJamie Gritton 			    strerror(errno));
10385264032fSJamie Gritton 			return -1;
10395264032fSJamie Gritton 		}
10405264032fSJamie Gritton 		if (st.st_ino != mpst.st_ino) {
10415264032fSJamie Gritton 			jail_warnx(j, "%s: %s: not a mount point",
10425264032fSJamie Gritton 			    pname, path);
10435264032fSJamie Gritton 			return -1;
10445264032fSJamie Gritton 		}
10455264032fSJamie Gritton 		if (strcmp(stfs.f_fstypename, umount_type)) {
10465264032fSJamie Gritton 			jail_warnx(j, "%s: %s: not a %s mount",
10475264032fSJamie Gritton 			    pname, path, umount_type);
10485264032fSJamie Gritton 			return -1;
10495264032fSJamie Gritton 		}
10505264032fSJamie Gritton 	}
10518ebbf0e2SJamie Gritton 	return 0;
10528ebbf0e2SJamie Gritton }
1053