command.c revision 223368
1109864Sjeff/*-
2109864Sjeff * Copyright (c) 2011 James Gritton
3109864Sjeff * All rights reserved.
4109864Sjeff *
5109864Sjeff * Redistribution and use in source and binary forms, with or without
6109864Sjeff * modification, are permitted provided that the following conditions
7109864Sjeff * are met:
8109864Sjeff * 1. Redistributions of source code must retain the above copyright
9109864Sjeff *    notice, this list of conditions and the following disclaimer.
10109864Sjeff * 2. Redistributions in binary form must reproduce the above copyright
11109864Sjeff *    notice, this list of conditions and the following disclaimer in the
12109864Sjeff *    documentation and/or other materials provided with the distribution.
13109864Sjeff *
14109864Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15109864Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16109864Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17109864Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18109864Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19109864Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20109864Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21109864Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22109864Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23109864Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24109864Sjeff * SUCH DAMAGE.
25109864Sjeff */
26109864Sjeff
27109864Sjeff#include <sys/cdefs.h>
28109864Sjeff__FBSDID("$FreeBSD: projects/jailconf/usr.sbin/jail/command.c 223368 2011-06-21 19:13:48Z jamie $");
29109864Sjeff
30109864Sjeff#include <sys/types.h>
31109864Sjeff#include <sys/event.h>
32109864Sjeff#include <sys/mount.h>
33109864Sjeff#include <sys/stat.h>
34109864Sjeff#include <sys/sysctl.h>
35109864Sjeff#include <sys/user.h>
36109864Sjeff#include <sys/wait.h>
37109864Sjeff
38109864Sjeff#include <err.h>
39109864Sjeff#include <errno.h>
40109864Sjeff#include <fcntl.h>
41109864Sjeff#include <kvm.h>
42109864Sjeff#include <login_cap.h>
43109864Sjeff#include <paths.h>
44109864Sjeff#include <pwd.h>
45109864Sjeff#include <signal.h>
46109864Sjeff#include <stdio.h>
47109864Sjeff#include <stdlib.h>
48109864Sjeff#include <string.h>
49109864Sjeff#include <unistd.h>
50109864Sjeff
51109864Sjeff#include "jailp.h"
52109864Sjeff
53109864Sjeff#define DEFAULT_STOP_TIMEOUT	10
54109864Sjeff#define PHASH_SIZE		256
55109864Sjeff
56109864SjeffLIST_HEAD(phhead, phash);
57109864Sjeff
58109864Sjeffstruct phash {
59109864Sjeff	LIST_ENTRY(phash)	le;
60109864Sjeff	struct cfjail		*j;
61109864Sjeff	pid_t			pid;
62109864Sjeff};
63109864Sjeff
64109864Sjeffint paralimit = -1;
65109864Sjeff
66109864Sjeffextern char **environ;
67109864Sjeff
68109864Sjeffstatic int run_command(struct cfjail *j);
69109864Sjeffstatic void add_proc(struct cfjail *j, pid_t pid);
70109864Sjeffstatic void clear_procs(struct cfjail *j);
71109864Sjeffstatic struct cfjail *find_proc(pid_t pid);
72109864Sjeffstatic int term_procs(struct cfjail *j);
73109864Sjeffstatic int get_user_info(struct cfjail *j, const char *username,
74109864Sjeff    const struct passwd **pwdp, login_cap_t **lcapp);
75109864Sjeffstatic int check_path(struct cfjail *j, const char *pname, const char *path,
76109864Sjeff    int isfile, const char *umount_type);
77109864Sjeff
78109864Sjeffstatic struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping);
79109864Sjeffstatic struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable);
80109864Sjeffstatic struct cfstring dummystring = { .len = 1 };
81109864Sjeffstatic struct phhead phash[PHASH_SIZE];
82109864Sjeffstatic int kq;
83109864Sjeff
84109864Sjeff/*
85109864Sjeff * Run the next command associated with a jail.
86109864Sjeff */
87109864Sjeffint
88109864Sjeffnext_command(struct cfjail *j)
89109864Sjeff{
90109864Sjeff	enum intparam comparam;
91109864Sjeff	int rval, create_failed;
92109864Sjeff
93109864Sjeff	rval = 0;
94109864Sjeff	create_failed = (j->flags & (JF_STOP | JF_FAILED)) == JF_FAILED;
95109864Sjeff	for (; (comparam = *j->comparam);
96109864Sjeff	     j->comparam += create_failed ? -1 : 1) {
97109864Sjeff		if (j->comstring == NULL) {
98109864Sjeff			switch (comparam) {
99109864Sjeff			case IP_MOUNT_DEVFS:
100109864Sjeff				if (!bool_param(j->intparams[IP_MOUNT_DEVFS]))
101109864Sjeff					continue;
102109864Sjeff				/* FALLTHROUGH */
103109864Sjeff			case IP__OP:
104109864Sjeff			case IP_STOP_TIMEOUT:
105109864Sjeff				j->comstring = &dummystring;
106109864Sjeff				break;
107109864Sjeff			default:
108109864Sjeff				if (j->intparams[comparam] == NULL)
109109864Sjeff					continue;
110109864Sjeff				j->comstring = create_failed
111109864Sjeff				    ? TAILQ_LAST(&j->intparams[comparam]->val,
112109864Sjeff					cfstrings)
113109864Sjeff				    : TAILQ_FIRST(&j->intparams[comparam]->val);
114109864Sjeff			}
115109864Sjeff		}
116109864Sjeff		for (; j->comstring != NULL;
117109864Sjeff		     j->comstring = j->comstring == &dummystring ? NULL :
118109864Sjeff			create_failed
119109864Sjeff			? TAILQ_PREV(j->comstring, cfstrings, tq)
120109864Sjeff			: TAILQ_NEXT(j->comstring, tq)) {
121109864Sjeff			if (rval != 0)
122109864Sjeff				return rval;
123109864Sjeff			if (j->comstring->len == 0 || (create_failed &&
124109864Sjeff			    (comparam == IP_EXEC_PRESTART || comparam ==
125109864Sjeff			    IP_EXEC_START || comparam == IP_COMMAND ||
126109864Sjeff			    comparam == IP_EXEC_POSTSTART)))
127109864Sjeff				continue;
128109864Sjeff			if (paralimit == 0) {
129109864Sjeff				requeue(j, &runnable);
130109864Sjeff				return 1;
131109864Sjeff			}
132109864Sjeff			rval = run_command(j);
133109864Sjeff			create_failed =
134109864Sjeff			    (j->flags & (JF_STOP | JF_FAILED)) == JF_FAILED;
135109864Sjeff		}
136109864Sjeff	}
137109864Sjeff	return rval;
138109864Sjeff}
139109864Sjeff
140109864Sjeff/*
141109864Sjeff * Check command exit status
142109864Sjeff */
143109864Sjeffint
144109864Sjefffinish_command(struct cfjail *j)
145109864Sjeff{
146109864Sjeff	int error;
147109864Sjeff
148109864Sjeff	if (!(j->flags & JF_SLEEPQ))
149109864Sjeff		return 0;
150109864Sjeff	j->flags &= ~JF_SLEEPQ;
151109864Sjeff	if (*j->comparam != IP_STOP_TIMEOUT) {
152109864Sjeff		paralimit++;
153109864Sjeff		if (!TAILQ_EMPTY(&runnable))
154109864Sjeff			requeue(TAILQ_FIRST(&runnable), &ready);
155109864Sjeff	}
156109864Sjeff	error = 0;
157109864Sjeff	if (j->flags & JF_TIMEOUT) {
158109864Sjeff		j->flags &= ~JF_TIMEOUT;
159109864Sjeff		if (*j->comparam != IP_STOP_TIMEOUT) {
160109864Sjeff			jail_warnx(j, "%s: timed out", j->comline);
161109864Sjeff			failed(j);
162109864Sjeff			error = -1;
163109864Sjeff		} else if (verbose > 0)
164109864Sjeff			jail_note(j, "timed out\n");
165109864Sjeff	} else if (j->pstatus != 0) {
166109864Sjeff		if (WIFSIGNALED(j->pstatus))
167109864Sjeff			jail_warnx(j, "%s: exited on signal %d",
168109864Sjeff			    j->comline, WTERMSIG(j->pstatus));
169109864Sjeff		else
170109864Sjeff			jail_warnx(j, "%s: failed", j->comline);
171109864Sjeff		j->pstatus = 0;
172109864Sjeff		failed(j);
173109864Sjeff		error = -1;
174109864Sjeff	}
175109864Sjeff	free(j->comline);
176109864Sjeff	j->comline = NULL;
177109864Sjeff	return error;
178109864Sjeff}
179110028Sjeff
180109864Sjeff/*
181110028Sjeff * Check for finished processed or timeouts.
182110028Sjeff */
183110028Sjeffstruct cfjail *
184110028Sjeffnext_proc(int nonblock)
185110028Sjeff{
186110028Sjeff	struct kevent ke;
187110028Sjeff	struct timespec ts;
188109864Sjeff	struct timespec *tsp;
189109864Sjeff	struct cfjail *j;
190109864Sjeff
191109864Sjeff	if (!TAILQ_EMPTY(&sleeping)) {
192109864Sjeff	again:
193109864Sjeff		tsp = NULL;
194110028Sjeff		if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) {
195110028Sjeff			clock_gettime(CLOCK_REALTIME, &ts);
196110028Sjeff			ts.tv_sec = j->timeout.tv_sec - ts.tv_sec;
197109864Sjeff			ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec;
198110028Sjeff			if (ts.tv_nsec < 0) {
199110028Sjeff				ts.tv_sec--;
200110028Sjeff				ts.tv_nsec += 1000000000;
201110028Sjeff			}
202110028Sjeff			if (ts.tv_sec < 0 ||
203110028Sjeff			    (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
204110028Sjeff				j->flags |= JF_TIMEOUT;
205110028Sjeff				clear_procs(j);
206110028Sjeff				return j;
207110028Sjeff			}
208109864Sjeff			tsp = &ts;
209109864Sjeff		}
210109864Sjeff		if (nonblock) {
211109864Sjeff			ts.tv_sec = 0;
212109864Sjeff			ts.tv_nsec = 0;
213109864Sjeff			tsp = &ts;
214110028Sjeff		}
215110028Sjeff		switch (kevent(kq, NULL, 0, &ke, 1, tsp)) {
216109864Sjeff		case -1:
217109864Sjeff			if (errno != EINTR)
218109864Sjeff				err(1, "kevent");
219109864Sjeff			goto again;
220109864Sjeff		case 0:
221109864Sjeff			if (!nonblock) {
222109864Sjeff				j = TAILQ_FIRST(&sleeping);
223109864Sjeff				j->flags |= JF_TIMEOUT;
224109864Sjeff				clear_procs(j);
225109864Sjeff				return j;
226109864Sjeff			}
227109864Sjeff			break;
228109864Sjeff		case 1:
229109864Sjeff			(void)waitpid(ke.ident, NULL, WNOHANG);
230109864Sjeff			if ((j = find_proc(ke.ident))) {
231109864Sjeff				j->pstatus = ke.data;
232109864Sjeff				return j;
233109864Sjeff			}
234109864Sjeff			goto again;
235109864Sjeff		}
236109864Sjeff	}
237109864Sjeff	return NULL;
238109864Sjeff}
239109864Sjeff
240109864Sjeff/*
241109864Sjeff * Run a single command for a jail, possible inside the jail.
242109864Sjeff */
243109864Sjeffint
244109864Sjeffrun_command(struct cfjail *j)
245109864Sjeff{
246109864Sjeff	const struct passwd *pwd;
247109864Sjeff	const struct cfstring *comstring, *s;
248109864Sjeff	login_cap_t *lcap;
249109864Sjeff	char **argv;
250109864Sjeff	char *cs, *comcs, *devpath;
251109864Sjeff	const char *jidstr, *conslog, *path, *ruleset, *term, *username;
252109864Sjeff	enum intparam comparam;
253109864Sjeff	size_t comlen;
254109864Sjeff	pid_t pid;
255109864Sjeff	int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout;
256109864Sjeff#if defined(INET) || defined(INET6)
257109864Sjeff	char *addr;
258109864Sjeff#endif
259109864Sjeff
260109864Sjeff	static char *cleanenv;
261109864Sjeff
262109864Sjeff	/* Perform some operations that aren't actually commands */
263109864Sjeff	comparam = *j->comparam;
264109864Sjeff	down = j->flags & (JF_STOP | JF_FAILED);
265109864Sjeff	switch (comparam) {
266109864Sjeff	case IP_STOP_TIMEOUT:
267109864Sjeff		return term_procs(j);
268109864Sjeff
269109864Sjeff	case IP__OP:
270109864Sjeff		if (down) {
271109864Sjeff			if (jail_remove(j->jid) == 0 && verbose >= 0 &&
272109864Sjeff			    (verbose > 0 || (j->flags & JF_STOP
273109864Sjeff			    ? note_remove : j->name != NULL)))
274109864Sjeff			    jail_note(j, "removed\n");
275109864Sjeff			j->jid = -1;
276109864Sjeff			if (j->flags & JF_STOP)
277109864Sjeff				dep_done(j, DF_LIGHT);
278109864Sjeff			else
279109864Sjeff				j->flags &= ~JF_PERSIST;
280109864Sjeff		} else {
281109864Sjeff			if (create_jail(j) < 0) {
282109864Sjeff				failed(j);
283109864Sjeff				return -1;
284109864Sjeff			}
285109864Sjeff			if (verbose >= 0 && (j->name || verbose > 0))
286109864Sjeff				jail_note(j, "created\n");
287109864Sjeff			dep_done(j, DF_LIGHT);
288109864Sjeff		}
289109864Sjeff		requeue(j, &ready);
290109864Sjeff		return 1;
291109864Sjeff
292109864Sjeff	default: ;
293109864Sjeff	}
294109864Sjeff	/*
295109864Sjeff	 * Collect exec arguments.  Internal commands for network and
296109864Sjeff	 * mounting build their own argument lists.
297109864Sjeff	 */
298109864Sjeff	comstring = j->comstring;
299109864Sjeff	bg = 0;
300110028Sjeff	switch (comparam) {
301110028Sjeff#ifdef INET
302109864Sjeff	case IP__IP4_IFADDR:
303109864Sjeff		argv = alloca(8 * sizeof(char *));
304109864Sjeff		*(const char **)&argv[0] = _PATH_IFCONFIG;
305109864Sjeff		if ((cs = strchr(comstring->s, '|'))) {
306109864Sjeff			argv[1] = alloca(cs - comstring->s + 1);
307109864Sjeff			strlcpy(argv[1], comstring->s, cs - comstring->s + 1);
308110028Sjeff			addr = cs + 1;
309110028Sjeff		} else {
310109864Sjeff			*(const char **)&argv[1] =
311109864Sjeff			    string_param(j->intparams[IP_INTERFACE]);
312109864Sjeff			addr = comstring->s;
313109864Sjeff		}
314110028Sjeff		*(const char **)&argv[2] = "inet";
315110028Sjeff		if (!(cs = strchr(addr, '/'))) {
316109864Sjeff			argv[3] = addr;
317110028Sjeff			*(const char **)&argv[4] = "netmask";
318109864Sjeff			*(const char **)&argv[5] = "255.255.255.255";
319109864Sjeff			argc = 6;
320109864Sjeff		} else if (strchr(cs + 1, '.')) {
321109864Sjeff			argv[3] = alloca(cs - addr + 1);
322109864Sjeff			strlcpy(argv[3], addr, cs - addr + 1);
323109864Sjeff			*(const char **)&argv[4] = "netmask";
324109864Sjeff			*(const char **)&argv[5] = cs + 1;
325109864Sjeff			argc = 6;
326109864Sjeff		} else {
327109864Sjeff			argv[3] = addr;
328109864Sjeff			argc = 4;
329109864Sjeff		}
330109864Sjeff		*(const char **)&argv[argc] = down ? "-alias" : "alias";
331109864Sjeff		argv[argc + 1] = NULL;
332109864Sjeff		break;
333109864Sjeff#endif
334109864Sjeff
335109864Sjeff#ifdef INET6
336109864Sjeff	case IP__IP6_IFADDR:
337109864Sjeff		argv = alloca(8 * sizeof(char *));
338109864Sjeff		*(const char **)&argv[0] = _PATH_IFCONFIG;
339109864Sjeff		if ((cs = strchr(comstring->s, '|'))) {
340109864Sjeff			argv[1] = alloca(cs - comstring->s + 1);
341109864Sjeff			strlcpy(argv[1], comstring->s, cs - comstring->s + 1);
342109864Sjeff			addr = cs + 1;
343109864Sjeff		} else {
344109864Sjeff			*(const char **)&argv[1] =
345109864Sjeff			    string_param(j->intparams[IP_INTERFACE]);
346109864Sjeff			addr = comstring->s;
347109864Sjeff		}
348109864Sjeff		*(const char **)&argv[2] = "inet6";
349109864Sjeff		argv[3] = addr;
350109864Sjeff		if (!(cs = strchr(addr, '/'))) {
351109864Sjeff			*(const char **)&argv[4] = "prefixlen";
352109864Sjeff			*(const char **)&argv[5] = "128";
353109864Sjeff			argc = 6;
354109864Sjeff		} else
355109864Sjeff			argc = 4;
356109864Sjeff		*(const char **)&argv[argc] = down ? "-alias" : "alias";
357109864Sjeff		argv[argc + 1] = NULL;
358109864Sjeff		break;
359109864Sjeff#endif
360109864Sjeff
361109864Sjeff	case IP_VNET_INTERFACE:
362109864Sjeff		argv = alloca(5 * sizeof(char *));
363109864Sjeff		*(const char **)&argv[0] = _PATH_IFCONFIG;
364109864Sjeff		argv[1] = comstring->s;
365109864Sjeff		*(const char **)&argv[2] = down ? "-vnet" : "vnet";
366109864Sjeff		jidstr = string_param(j->intparams[KP_JID]);
367109864Sjeff		*(const char **)&argv[3] =
368109864Sjeff			jidstr ? jidstr : string_param(j->intparams[KP_NAME]);
369109864Sjeff		argv[4] = NULL;
370109864Sjeff		break;
371109864Sjeff
372109864Sjeff	case IP_MOUNT:
373109864Sjeff	case IP__MOUNT_FROM_FSTAB:
374109864Sjeff		argv = alloca(8 * sizeof(char *));
375109864Sjeff		comcs = alloca(comstring->len + 1);
376109864Sjeff		strcpy(comcs, comstring->s);
377109864Sjeff		argc = 0;
378109864Sjeff		for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4;
379109864Sjeff		     cs = strtok(NULL, " \t\f\v\r\n"))
380109864Sjeff			argv[argc++] = cs;
381109864Sjeff		if (argc == 0)
382109864Sjeff			return 0;
383109864Sjeff		if (argc < 3) {
384109864Sjeff			jail_warnx(j, "%s: %s: missing information",
385109864Sjeff			    j->intparams[comparam]->name, comstring->s);
386109864Sjeff			failed(j);
387109864Sjeff			return -1;
388109864Sjeff		}
389109864Sjeff		if (check_path(j, j->intparams[comparam]->name, argv[1], 0,
390109864Sjeff		    down ? argv[2] : NULL) < 0) {
391109864Sjeff			failed(j);
392109864Sjeff			return -1;
393109864Sjeff		}
394109864Sjeff		if (down) {
395109864Sjeff			argv[4] = NULL;
396109864Sjeff			argv[3] = argv[1];
397109864Sjeff			*(const char **)&argv[0] = "/sbin/umount";
398109864Sjeff		} else {
399109864Sjeff			if (argc == 4) {
400109864Sjeff				argv[7] = NULL;
401109864Sjeff				argv[6] = argv[1];
402109864Sjeff				argv[5] = argv[0];
403109864Sjeff				argv[4] = argv[3];
404109864Sjeff				*(const char **)&argv[3] = "-o";
405109864Sjeff			} else {
406109864Sjeff				argv[5] = NULL;
407109864Sjeff				argv[4] = argv[1];
408109864Sjeff				argv[3] = argv[0];
409109864Sjeff			}
410109864Sjeff			*(const char **)&argv[0] = _PATH_MOUNT;
411109864Sjeff		}
412109864Sjeff		*(const char **)&argv[1] = "-t";
413109864Sjeff		break;
414109864Sjeff
415109864Sjeff	case IP_MOUNT_DEVFS:
416110028Sjeff		path = string_param(j->intparams[KP_PATH]);
417110028Sjeff		if (path == NULL) {
418110028Sjeff			jail_warnx(j, "mount.devfs: no path");
419110028Sjeff			failed(j);
420109864Sjeff			return -1;
421109864Sjeff		}
422109864Sjeff		devpath = alloca(strlen(path) + 5);
423109864Sjeff		sprintf(devpath, "%s/dev", path);
424109864Sjeff		if (check_path(j, "mount.devfs", devpath, 0,
425109864Sjeff		    down ? "devfs" : NULL) < 0) {
426109864Sjeff			failed(j);
427109864Sjeff			return -1;
428109864Sjeff		}
429109864Sjeff		if (down) {
430109864Sjeff			argv = alloca(3 * sizeof(char *));
431109864Sjeff			*(const char **)&argv[0] = "/sbin/umount";
432109864Sjeff			argv[1] = devpath;
433109864Sjeff			argv[2] = NULL;
434109864Sjeff		} else {
435109864Sjeff			argv = alloca(4 * sizeof(char *));
436109864Sjeff			*(const char **)&argv[0] = _PATH_BSHELL;
437109864Sjeff			*(const char **)&argv[1] = "-c";
438109864Sjeff			ruleset = string_param(j->intparams
439109864Sjeff			    [IP_MOUNT_DEVFS_RULESET]);
440109864Sjeff			argv[2] = alloca(strlen(path) +
441109864Sjeff			    (ruleset ? strlen(ruleset) + 1 : 0) + 56);
442110028Sjeff			sprintf(argv[2], ". /etc/rc.subr; load_rc_config .; "
443110028Sjeff			    "devfs_mount_jail %s/dev%s%s", path,
444110028Sjeff			    ruleset ? " " : "", ruleset ? ruleset : "");
445110028Sjeff			argv[3] = NULL;
446109864Sjeff		}
447109864Sjeff		break;
448109864Sjeff
449109864Sjeff	case IP_COMMAND:
450109864Sjeff		if (j->name != NULL)
451109864Sjeff			goto default_command;
452109864Sjeff		argc = 0;
453109864Sjeff		TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
454109864Sjeff			argc++;
455109864Sjeff		argv = alloca((argc + 1) * sizeof(char *));
456109864Sjeff		argc = 0;
457109864Sjeff		TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
458109864Sjeff			argv[argc++] = s->s;
459109864Sjeff		argv[argc] = NULL;
460109864Sjeff		j->comstring = &dummystring;
461109864Sjeff		break;
462109864Sjeff
463109864Sjeff	default:
464109864Sjeff	default_command:
465109864Sjeff		if ((cs = strpbrk(comstring->s, "!\"$&'()*;<>?[\\]`{|}~")) &&
466109864Sjeff		    !(cs[0] == '&' && cs[1] == '\0')) {
467109864Sjeff			argv = alloca(4 * sizeof(char *));
468109864Sjeff			*(const char **)&argv[0] = _PATH_BSHELL;
469109970Sjeff			*(const char **)&argv[1] = "-c";
470109970Sjeff			argv[2] = comstring->s;
471109970Sjeff			argv[3] = NULL;
472109970Sjeff		} else {
473109970Sjeff			if (cs) {
474109970Sjeff				*cs = 0;
475109864Sjeff				bg = 1;
476109970Sjeff			}
477109864Sjeff			comcs = alloca(comstring->len + 1);
478109864Sjeff			strcpy(comcs, comstring->s);
479109864Sjeff			argc = 0;
480109864Sjeff			for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
481109864Sjeff			     cs = strtok(NULL, " \t\f\v\r\n"))
482109864Sjeff				argc++;
483109864Sjeff			argv = alloca((argc + 1) * sizeof(char *));
484109864Sjeff			strcpy(comcs, comstring->s);
485109864Sjeff			argc = 0;
486109864Sjeff			for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
487109864Sjeff			     cs = strtok(NULL, " \t\f\v\r\n"))
488109864Sjeff				argv[argc++] = cs;
489109864Sjeff			argv[argc] = NULL;
490109864Sjeff		}
491109864Sjeff	}
492109864Sjeff	if (argv[0] == NULL)
493109864Sjeff		return 0;
494109864Sjeff
495109864Sjeff	if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) &&
496109864Sjeff	    timeout != 0) {
497109864Sjeff		clock_gettime(CLOCK_REALTIME, &j->timeout);
498109864Sjeff		j->timeout.tv_sec += timeout;
499109864Sjeff	} else
500109864Sjeff		j->timeout.tv_sec = 0;
501109864Sjeff
502109864Sjeff	injail = comparam == IP_EXEC_START || comparam == IP_COMMAND ||
503109864Sjeff	    comparam == IP_EXEC_STOP;
504110028Sjeff	clean = bool_param(j->intparams[IP_EXEC_CLEAN]);
505109864Sjeff	username = string_param(j->intparams[injail
506109864Sjeff	    ? IP_EXEC_JAIL_USER : IP_EXEC_SYSTEM_USER]);
507109864Sjeff	sjuser = bool_param(j->intparams[IP_EXEC_SYSTEM_JAIL_USER]);
508109864Sjeff
509109864Sjeff	consfd = 0;
510109864Sjeff	if (injail &&
511110028Sjeff	    (conslog = string_param(j->intparams[IP_EXEC_CONSOLELOG]))) {
512110028Sjeff		if (check_path(j, "exec.consolelog", conslog, 1, NULL) < 0) {
513110028Sjeff			failed(j);
514110028Sjeff			return -1;
515109971Sjeff		}
516109971Sjeff		consfd =
517109971Sjeff		    open(conslog, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE);
518109971Sjeff		if (consfd < 0) {
519109971Sjeff			jail_warnx(j, "open %s: %s", conslog, strerror(errno));
520109971Sjeff			failed(j);
521110028Sjeff			return -1;
522109864Sjeff		}
523110028Sjeff	}
524110028Sjeff
525110028Sjeff	comlen = 0;
526110028Sjeff	for (i = 0; argv[i]; i++)
527110028Sjeff		comlen += strlen(argv[i]) + 1;
528110028Sjeff	j->comline = cs = emalloc(comlen);
529109970Sjeff	for (i = 0; argv[i]; i++) {
530109970Sjeff		strcpy(cs, argv[i]);
531109864Sjeff		if (argv[i + 1]) {
532110028Sjeff			cs += strlen(argv[i]) + 1;
533109864Sjeff			cs[-1] = ' ';
534109864Sjeff		}
535109864Sjeff	}
536109864Sjeff	if (verbose > 0)
537109864Sjeff		jail_note(j, "run command%s%s%s: %s\n",
538109864Sjeff		    injail ? " in jail" : "", username ? " as " : "",
539109864Sjeff		    username ? username : "", j->comline);
540109864Sjeff
541109864Sjeff	pid = fork();
542109864Sjeff	if (pid < 0)
543109864Sjeff		err(1, "fork");
544109864Sjeff	if (pid > 0) {
545109864Sjeff		if (bg) {
546109864Sjeff			free(j->comline);
547109864Sjeff			j->comline = NULL;
548109864Sjeff			requeue(j, &ready);
549109864Sjeff		} else {
550109864Sjeff			paralimit--;
551109864Sjeff			add_proc(j, pid);
552109864Sjeff		}
553109864Sjeff		return 1;
554109864Sjeff	}
555109864Sjeff	if (bg)
556109864Sjeff		setsid();
557109864Sjeff
558109864Sjeff	/* Set up the environment and run the command */
559109864Sjeff	pwd = NULL;
560110028Sjeff	lcap = NULL;
561109864Sjeff	if ((clean || username) && injail && sjuser &&
562110028Sjeff	    get_user_info(j, username, &pwd, &lcap) < 0)
563109970Sjeff		exit(1);
564109970Sjeff	if (injail) {
565110028Sjeff		/* jail_attach won't chdir along with its chroot. */
566110028Sjeff		path = string_param(j->intparams[KP_PATH]);
567110028Sjeff		if (path && chdir(path) < 0) {
568110028Sjeff			jail_warnx(j, "chdir %s: %s", path, strerror(errno));
569109970Sjeff			exit(1);
570109970Sjeff		}
571109970Sjeff		if (int_param(j->intparams[IP_EXEC_FIB], &fib) &&
572109970Sjeff		    setfib(fib) < 0) {
573109970Sjeff			jail_warnx(j, "setfib: %s", strerror(errno));
574109970Sjeff			exit(1);
575110028Sjeff		}
576110028Sjeff		if (jail_attach(j->jid) < 0) {
577109970Sjeff			jail_warnx(j, "jail_attach: %s", strerror(errno));
578109970Sjeff			exit(1);
579109970Sjeff		}
580109970Sjeff	}
581109970Sjeff	if (clean || username) {
582109864Sjeff		if (!(injail && sjuser) &&
583109864Sjeff		    get_user_info(j, username, &pwd, &lcap) < 0)
584109864Sjeff			exit(1);
585109864Sjeff		if (clean) {
586109864Sjeff			term = getenv("TERM");
587109864Sjeff			environ = &cleanenv;
588109864Sjeff			setenv("PATH", "/bin:/usr/bin", 0);
589109864Sjeff			setenv("TERM", term, 1);
590109864Sjeff		}
591109864Sjeff		if (setusercontext(lcap, pwd, pwd->pw_uid, username
592109864Sjeff		    ? LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN
593109864Sjeff		    : LOGIN_SETPATH | LOGIN_SETENV) < 0) {
594109864Sjeff			jail_warnx(j, "setusercontext %s: %s", pwd->pw_name,
595109864Sjeff			    strerror(errno));
596109864Sjeff			exit(1);
597109864Sjeff		}
598109864Sjeff		login_close(lcap);
599110028Sjeff		setenv("USER", pwd->pw_name, 1);
600109864Sjeff		setenv("HOME", pwd->pw_dir, 1);
601109864Sjeff		setenv("SHELL",
602109864Sjeff		    *pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL, 1);
603109864Sjeff		if (clean && chdir(pwd->pw_dir) < 0) {
604109864Sjeff			jail_warnx(j, "chdir %s: %s",
605109864Sjeff			    pwd->pw_dir, strerror(errno));
606109864Sjeff			exit(1);
607109864Sjeff		}
608109864Sjeff		endpwent();
609109864Sjeff	}
610109970Sjeff
611109970Sjeff	if (consfd != 0 && (dup2(consfd, 1) < 0 || dup2(consfd, 2) < 0)) {
612109970Sjeff		jail_warnx(j, "exec.consolelog: %s", strerror(errno));
613109970Sjeff		exit(1);
614109970Sjeff	}
615109970Sjeff	closefrom(3);
616109970Sjeff	execvp(argv[0], argv);
617110028Sjeff	jail_warnx(j, "exec %s: %s", argv[0], strerror(errno));
618109970Sjeff	exit(1);
619109970Sjeff}
620110028Sjeff
621110028Sjeff/*
622109970Sjeff * Add a process to the hash, tied to a jail.
623109864Sjeff */
624109864Sjeffstatic void
625110028Sjeffadd_proc(struct cfjail *j, pid_t pid)
626109864Sjeff{
627109864Sjeff	struct kevent ke;
628109864Sjeff	struct cfjail *tj;
629109970Sjeff	struct phash *ph;
630109970Sjeff
631109970Sjeff	if (!kq && (kq = kqueue()) < 0)
632110028Sjeff		err(1, "kqueue");
633109970Sjeff	EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
634109970Sjeff	if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)
635110028Sjeff		err(1, "kevent");
636110028Sjeff	ph = emalloc(sizeof(struct phash));
637109970Sjeff	ph->j = j;
638109970Sjeff	ph->pid = pid;
639109970Sjeff	LIST_INSERT_HEAD(&phash[pid % PHASH_SIZE], ph, le);
640109970Sjeff	j->nprocs++;
641110028Sjeff	j->flags |= JF_SLEEPQ;
642110028Sjeff	if (j->timeout.tv_sec == 0)
643109970Sjeff		requeue(j, &sleeping);
644110028Sjeff	else {
645110028Sjeff		/* File the jail in the sleep queue acording to its timeout. */
646110028Sjeff		TAILQ_REMOVE(j->queue, j, tq);
647109970Sjeff		TAILQ_FOREACH(tj, &sleeping, tq) {
648109970Sjeff			if (!tj->timeout.tv_sec ||
649109970Sjeff			    j->timeout.tv_sec < tj->timeout.tv_sec ||
650109970Sjeff			    (j->timeout.tv_sec == tj->timeout.tv_sec &&
651110028Sjeff			    j->timeout.tv_nsec <= tj->timeout.tv_nsec)) {
652110028Sjeff				TAILQ_INSERT_BEFORE(tj, j, tq);
653110028Sjeff				break;
654109970Sjeff			}
655109970Sjeff		}
656109970Sjeff		if (tj == NULL)
657110028Sjeff			TAILQ_INSERT_TAIL(&sleeping, j, tq);
658109970Sjeff		j->queue = &sleeping;
659109970Sjeff	}
660109970Sjeff}
661109970Sjeff
662109864Sjeff/*
663109864Sjeff * Remove any processes from the hash that correspond to a jail.
664109864Sjeff */
665109864Sjeffstatic void
666109864Sjeffclear_procs(struct cfjail *j)
667109864Sjeff{
668109864Sjeff	struct kevent ke;
669109864Sjeff	struct phash *ph, *tph;
670109864Sjeff	int i;
671109864Sjeff
672109864Sjeff	j->nprocs = 0;
673109864Sjeff	for (i = 0; i < PHASH_SIZE; i++)
674109864Sjeff		LIST_FOREACH_SAFE(ph, &phash[i], le, tph)
675109864Sjeff			if (ph->j == j) {
676109864Sjeff				EV_SET(&ke, ph->pid, EVFILT_PROC, EV_DELETE,
677109864Sjeff				    NOTE_EXIT, 0, NULL);
678109864Sjeff				(void)kevent(kq, &ke, 1, NULL, 0, NULL);
679109864Sjeff				LIST_REMOVE(ph, le);
680109864Sjeff				free(ph);
681109970Sjeff			}
682109970Sjeff}
683110028Sjeff
684109864Sjeff/*
685109864Sjeff * Find the jail that corresponds to an exited process.
686109864Sjeff */
687109864Sjeffstatic struct cfjail *
688109864Sjefffind_proc(pid_t pid)
689109864Sjeff{
690109864Sjeff	struct cfjail *j;
691109864Sjeff	struct phash *ph;
692109864Sjeff
693110028Sjeff	LIST_FOREACH(ph, &phash[pid % PHASH_SIZE], le)
694109864Sjeff		if (ph->pid == pid) {
695109864Sjeff			j = ph->j;
696109864Sjeff			LIST_REMOVE(ph, le);
697109864Sjeff			free(ph);
698109864Sjeff			return --j->nprocs ? NULL : j;
699109864Sjeff		}
700109864Sjeff	return NULL;
701109864Sjeff}
702109864Sjeff
703109864Sjeff/*
704109864Sjeff * Send SIGTERM to all processes in a jail and wait for them to die.
705109864Sjeff */
706110028Sjeffstatic int
707109864Sjeffterm_procs(struct cfjail *j)
708109864Sjeff{
709109864Sjeff	struct kinfo_proc *ki;
710109864Sjeff	int i, noted, pcnt, timeout;
711109864Sjeff
712109864Sjeff	static kvm_t *kd;
713110226Sscottl
714109864Sjeff	if (!int_param(j->intparams[IP_STOP_TIMEOUT], &timeout))
715109864Sjeff		timeout = DEFAULT_STOP_TIMEOUT;
716110226Sscottl	else if (timeout == 0)
717109864Sjeff		return 0;
718109864Sjeff
719109864Sjeff	if (kd == NULL) {
720109864Sjeff		kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "jail");
721109864Sjeff		if (kd == NULL)
722109864Sjeff			exit(1);
723109864Sjeff	}
724109864Sjeff
725109864Sjeff	ki = kvm_getprocs(kd, KERN_PROC_PROC, 0, &pcnt);
726109864Sjeff	if (ki == NULL)
727110226Sscottl		exit(1);
728109864Sjeff	noted = 0;
729109864Sjeff	for (i = 0; i < pcnt; i++)
730109864Sjeff		if (ki[i].ki_jid == j->jid &&
731109864Sjeff		    kill(ki[i].ki_pid, SIGTERM) == 0) {
732109864Sjeff			add_proc(j, ki[i].ki_pid);
733109864Sjeff			if (verbose > 0) {
734109864Sjeff				if (!noted) {
735109864Sjeff					noted = 1;
736109864Sjeff					jail_note(j, "sent SIGTERM to:");
737109864Sjeff				}
738109864Sjeff				printf(" %d", ki[i].ki_pid);
739109864Sjeff			}
740109864Sjeff		}
741109864Sjeff	if (noted)
742109864Sjeff		printf("\n");
743109864Sjeff	if (j->nprocs > 0) {
744109864Sjeff		clock_gettime(CLOCK_REALTIME, &j->timeout);
745109864Sjeff		j->timeout.tv_sec += timeout;
746109864Sjeff		return 1;
747109864Sjeff	}
748109864Sjeff	return 0;
749109864Sjeff}
750109864Sjeff
751109864Sjeff/*
752109864Sjeff * Look up a user in the passwd and login.conf files.
753109864Sjeff */
754109864Sjeffstatic int
755109864Sjeffget_user_info(struct cfjail *j, const char *username,
756109864Sjeff    const struct passwd **pwdp, login_cap_t **lcapp)
757109864Sjeff{
758	const struct passwd *pwd;
759
760	*pwdp = pwd = username ? getpwnam(username) : getpwuid(getuid());
761	if (pwd == NULL) {
762		if (errno)
763			jail_warnx(j, "getpwnam%s%s: %s", username ? " " : "",
764			    username ? username : "", strerror(errno));
765		else if (username)
766			jail_warnx(j, "%s: no such user", username);
767		else
768			jail_warnx(j, "unknown uid %d", getuid());
769		return -1;
770	}
771	*lcapp = login_getpwclass(pwd);
772	if (*lcapp == NULL) {
773		jail_warnx(j, "getpwclass %s: %s", pwd->pw_name,
774		    strerror(errno));
775		return -1;
776	}
777	/* Set the groups while the group file is still available */
778	if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) {
779		jail_warnx(j, "initgroups %s: %s", pwd->pw_name,
780		    strerror(errno));
781		return -1;
782	}
783	return 0;
784}
785
786/*
787 * Make sure a mount or consolelog path is a valid absolute pathname
788 * with no symlinks.
789 */
790static int
791check_path(struct cfjail *j, const char *pname, const char *path, int isfile,
792    const char *umount_type)
793{
794	struct stat st, mpst;
795	struct statfs stfs;
796	char *tpath, *p;
797	const char *jailpath;
798	size_t jplen;
799
800	if (path[0] != '/') {
801		jail_warnx(j, "%s: %s: not an absolute pathname",
802		    pname, path);
803		return -1;
804	}
805	/*
806	 * Only check for symlinks in components below the jail's path,
807	 * since that's where the security risk lies.
808	 */
809	jailpath = string_param(j->intparams[KP_PATH]);
810	if (jailpath == NULL)
811		jailpath = "";
812	jplen = strlen(jailpath);
813	if (!strncmp(path, jailpath, jplen) && path[jplen] == '/') {
814		tpath = alloca(strlen(path) + 1);
815		strcpy(tpath, path);
816		for (p = tpath + jplen; p != NULL; ) {
817			p = strchr(p + 1, '/');
818			if (p)
819				*p = '\0';
820			if (lstat(tpath, &st) < 0) {
821				if (errno == ENOENT && isfile && !p)
822					break;
823				jail_warnx(j, "%s: %s: %s", pname, tpath,
824				    strerror(errno));
825				return -1;
826			}
827			if (S_ISLNK(st.st_mode)) {
828				jail_warnx(j, "%s: %s is a symbolic link",
829				    pname, tpath);
830				return -1;
831			}
832			if (p)
833				*p = '/';
834		}
835	}
836	if (umount_type != NULL) {
837		if (stat(path, &st) < 0 || statfs(path, &stfs) < 0) {
838			jail_warnx(j, "%s: %s: %s", pname, path,
839			    strerror(errno));
840			return -1;
841		}
842		if (stat(stfs.f_mntonname, &mpst) < 0) {
843			jail_warnx(j, "%s: %s: %s", pname, stfs.f_mntonname,
844			    strerror(errno));
845			return -1;
846		}
847		if (st.st_ino != mpst.st_ino) {
848			jail_warnx(j, "%s: %s: not a mount point",
849			    pname, path);
850			return -1;
851		}
852		if (strcmp(stfs.f_fstypename, umount_type)) {
853			jail_warnx(j, "%s: %s: not a %s mount",
854			    pname, path, umount_type);
855			return -1;
856		}
857	}
858	return 0;
859}
860