command.c revision 214797
1/*-
2 * Copyright (c) 2010 James Gritton
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: projects/jailconf/usr.sbin/jail/command.c 214797 2010-11-04 18:40:29Z jamie $");
29
30#include <sys/types.h>
31#include <sys/event.h>
32#include <sys/stat.h>
33#include <sys/sysctl.h>
34#include <sys/user.h>
35#include <sys/wait.h>
36
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <kvm.h>
41#include <login_cap.h>
42#include <paths.h>
43#include <pwd.h>
44#include <signal.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49
50#include "jailp.h"
51
52#define COMSTRING_DUMMY		((struct cfstring *)1)
53#define DEFAULT_STOP_TIMEOUT	10
54#define PHASH_SIZE		256
55
56LIST_HEAD(phhead, phash);
57
58struct phash {
59	LIST_ENTRY(phash)	le;
60	struct cfjail		*j;
61	pid_t			pid;
62};
63
64extern char **environ;
65
66static int get_user_info(struct cfjail *j, const char *username,
67    const struct passwd **pwdp, login_cap_t **lcapp);
68static void add_proc(struct cfjail *j, pid_t pid);
69static void clear_procs(struct cfjail *j);
70static struct cfjail *find_proc(pid_t pid);
71static int check_path(struct cfjail *j, const char *pname, const char *path,
72    int isfile);
73
74static struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping);
75static struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable);
76static struct phhead phash[PHASH_SIZE];
77static int kq;
78
79/*
80 * Run a command associated with a jail, possibly inside the jail.
81 */
82int
83run_command(struct cfjail *j, int *plimit, enum intparam comparam)
84{
85	const struct passwd *pwd;
86	struct cfstring *comstring, *s;
87	login_cap_t *lcap;
88	char **argv;
89	char *cs, *addr, *comcs, *devpath;
90	const char *jidstr, *conslog, *path, *ruleset, *term, *username;
91	size_t comlen;
92	pid_t pid;
93	int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout;
94
95	static char *cleanenv;
96
97	if (comparam) {
98		if (comparam == IP_MOUNT_DEVFS
99		    ? !bool_param(j->intparams[IP_MOUNT_DEVFS])
100		    : j->intparams[comparam] == NULL)
101			return 0;
102		j->comparam = comparam;
103		j->comstring = comparam == IP_MOUNT_DEVFS ? COMSTRING_DUMMY
104		    : STAILQ_FIRST(&j->intparams[comparam]->val);
105	} else {
106		comparam = j->comparam;
107		if (!(j->flags & JF_RUNQ))
108			j->comstring = j->comstring == COMSTRING_DUMMY
109			    ? NULL : STAILQ_NEXT(j->comstring, tq);
110	}
111	comstring = j->comstring;
112	if (comstring == NULL ||
113	    (comstring != COMSTRING_DUMMY && comstring->len == 0))
114		return 0;
115	if (plimit && *plimit == 0) {
116		j->flags |= JF_RUNQ;
117		requeue(j, &runnable);
118		return 1;
119	}
120	j->flags &= ~(JF_RUNQ | JF_BACKGROUND);
121	/*
122	 * Collect exec arguments.  Internal commands for network and
123	 * mounting build their own argument lists (XXX they should be
124	 * truly internal).
125	 */
126	bg = j->flags & JF_FAILED;
127	down = j->flags & (JF_STOP | JF_FAILED);
128	if (comparam == IP__IP4_IFADDR) {
129		argv = alloca(8 * sizeof(char *));
130		*(const char **)&argv[0] = _PATH_IFCONFIG;
131		if ((cs = strchr(comstring->s, '|'))) {
132			argv[1] = alloca(cs - comstring->s + 1);
133			strlcpy(argv[1], comstring->s, cs - comstring->s + 1);
134			addr = cs + 1;
135		} else {
136			*(const char **)&argv[1] =
137			    string_param(j->intparams[IP_INTERFACE]);
138			addr = comstring->s;
139		}
140		*(const char **)&argv[2] = "inet";
141		if (!(cs = strchr(addr, '/'))) {
142			argv[3] = addr;
143			*(const char **)&argv[4] = "netmask";
144			*(const char **)&argv[5] = "255.255.255.255";
145			argc = 6;
146		} else if (strchr(cs + 1, '.')) {
147			argv[3] = alloca(cs - addr + 1);
148			strlcpy(argv[3], addr, cs - addr + 1);
149			*(const char **)&argv[4] = "netmask";
150			*(const char **)&argv[5] = cs + 1;
151			argc = 6;
152		} else {
153			argv[3] = addr;
154			argc = 4;
155		}
156		*(const char **)&argv[argc] = down ? "-alias" : "alias";
157		argv[argc + 1] = NULL;
158		j->flags |= JF_IFUP;
159#ifdef INET6
160	} else if (comparam == IP__IP6_IFADDR) {
161		argv = alloca(8 * sizeof(char *));
162		*(const char **)&argv[0] = _PATH_IFCONFIG;
163		if ((cs = strchr(comstring->s, '|'))) {
164			argv[1] = alloca(cs - comstring->s + 1);
165			strlcpy(argv[1], comstring->s, cs - comstring->s + 1);
166			addr = cs + 1;
167		} else {
168			*(const char **)&argv[1] =
169			    string_param(j->intparams[IP_INTERFACE]);
170			addr = comstring->s;
171		}
172		*(const char **)&argv[2] = "inet6";
173		argv[3] = addr;
174		if (!(cs = strchr(addr, '/'))) {
175			*(const char **)&argv[4] = "prefixlen";
176			*(const char **)&argv[5] = "128";
177			argc = 6;
178		} else
179			argc = 4;
180		*(const char **)&argv[argc] = down ? "-alias" : "alias";
181		argv[argc + 1] = NULL;
182		j->flags |= JF_IFUP;
183#endif
184	} else if (comparam == IP_VNET_INTERFACE) {
185		argv = alloca(5 * sizeof(char *));
186		*(const char **)&argv[0] = _PATH_IFCONFIG;
187		argv[1] = comstring->s;
188		*(const char **)&argv[2] = down ? "-vnet" : "vnet";
189		jidstr = string_param(j->intparams[KP_JID]);
190		*(const char **)&argv[3] =
191			jidstr ? jidstr : string_param(j->intparams[KP_NAME]);
192		argv[4] = NULL;
193		j->flags |= JF_IFUP;
194	} else if (comparam == IP_MOUNT || comparam == IP__MOUNT_FROM_FSTAB) {
195		argv = alloca(8 * sizeof(char *));
196		comcs = alloca(comstring->len + 1);
197		strcpy(comcs, comstring->s);
198		argc = 0;
199		for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4;
200		     cs = strtok(NULL, " \t\f\v\r\n"))
201			argv[argc++] = cs;
202		if (argc == 0)
203			return 0;
204		if (argc < 3) {
205			jail_warnx(j, "%s: %s: missing information",
206			    j->intparams[comparam]->name, comstring->s);
207			failed(j);
208			return -1;
209		}
210		if (check_path(j, j->intparams[comparam]->name, argv[1], 0) < 0)
211			return -1;
212		if (down) {
213			argv[4] = NULL;
214			argv[3] = argv[1];
215			*(const char **)&argv[0] = "/sbin/umount";
216		} else {
217			if (argc == 4) {
218				argv[7] = NULL;
219				argv[6] = argv[1];
220				argv[5] = argv[0];
221				argv[4] = argv[3];
222				*(const char **)&argv[3] = "-o";
223			} else {
224				argv[5] = NULL;
225				argv[4] = argv[1];
226				argv[3] = argv[0];
227			}
228			*(const char **)&argv[0] = _PATH_MOUNT;
229		}
230		*(const char **)&argv[1] = "-t";
231		j->flags |= JF_MOUNTED;
232	} else if (comparam == IP_MOUNT_DEVFS) {
233		path = string_param(j->intparams[KP_PATH]);
234		if (path == NULL) {
235			jail_warnx(j, "mount.devfs: no path");
236			failed(j);
237			return -1;
238		}
239		devpath = alloca(strlen(path) + 5);
240		sprintf(devpath, "%s/dev", path);
241		if (check_path(j, "mount.devfs", devpath, 0) < 0)
242			return -1;
243		if (down) {
244			argv = alloca(3 * sizeof(char *));
245			*(const char **)&argv[0] = "/sbin/umount";
246			argv[1] = devpath;
247			argv[2] = NULL;
248		} else {
249			argv = alloca(4 * sizeof(char *));
250			*(const char **)&argv[0] = _PATH_BSHELL;
251			*(const char **)&argv[1] = "-c";
252			ruleset = string_param(j->intparams
253			    [IP_MOUNT_DEVFS_RULESET]);
254			argv[2] = alloca(strlen(path) +
255			    (ruleset ? strlen(ruleset) + 1 : 0) + 56);
256			sprintf(argv[2], ". /etc/rc.subr; load_rc_config .; "
257			    "devfs_mount_jail %s/dev%s%s", path,
258			    ruleset ? " " : "", ruleset ? ruleset : "");
259			argv[3] = NULL;
260		}
261		j->flags |= JF_MOUNTED;
262	} else if (comparam == IP_COMMAND && j->name == NULL) {
263		argc = 0;
264		STAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
265			argc++;
266		argv = alloca((argc + 1) * sizeof(char *));
267		argc = 0;
268		STAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
269			argv[argc++] = s->s;
270		argv[argc] = NULL;
271		j->comstring = NULL;
272	} else if ((cs = strpbrk(comstring->s, "!\"$&'()*;<>?[\\]`{|}~")) &&
273		   !(cs[0] == '&' && cs[1] == '\0')) {
274		argv = alloca(4 * sizeof(char *));
275		*(const char **)&argv[0] = _PATH_BSHELL;
276		*(const char **)&argv[1] = "-c";
277		argv[2] = comstring->s;
278		argv[3] = NULL;
279	} else {
280		if (cs) {
281			*cs = 0;
282			bg = 1;
283		}
284		comcs = alloca(comstring->len + 1);
285		strcpy(comcs, comstring->s);
286		argc = 0;
287		for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
288		     cs = strtok(NULL, " \t\f\v\r\n"))
289			argc++;
290		argv = alloca((argc + 1) * sizeof(char *));
291		strcpy(comcs, comstring->s);
292		argc = 0;
293		for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
294		     cs = strtok(NULL, " \t\f\v\r\n"))
295			argv[argc++] = cs;
296		argv[argc] = NULL;
297	}
298	if (argv[0] == NULL)
299		return 0;
300
301	j->pstatus = 0;
302	if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) &&
303	    timeout != 0) {
304		clock_gettime(CLOCK_REALTIME, &j->timeout);
305		j->timeout.tv_sec += timeout;
306	} else
307		j->timeout.tv_sec = 0;
308
309	injail = comparam == IP_EXEC_START || comparam == IP_COMMAND ||
310	    comparam == IP_EXEC_STOP;
311	clean = bool_param(j->intparams[IP_EXEC_CLEAN]);
312	username = string_param(j->intparams[injail
313	    ? IP_EXEC_JAIL_USER : IP_EXEC_SYSTEM_USER]);
314	sjuser = bool_param(j->intparams[IP_EXEC_SYSTEM_JAIL_USER]);
315
316	consfd = 0;
317	if (injail &&
318	    (conslog = string_param(j->intparams[IP_EXEC_CONSOLELOG]))) {
319		if (check_path(j, "exec.consolelog", conslog, 1) < 0)
320			return -1;
321		consfd =
322		    open(conslog, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE);
323		if (consfd < 0) {
324			jail_warnx(j, "open %s: %s", conslog, strerror(errno));
325			failed(j);
326			return -1;
327		}
328	}
329
330	comlen = 0;
331	for (i = 0; argv[i]; i++)
332		comlen += strlen(argv[i]) + 1;
333	j->comline = cs = emalloc(comlen);
334	for (i = 0; argv[i]; i++) {
335		strcpy(cs, argv[i]);
336		if (argv[i + 1]) {
337			cs += strlen(argv[i]) + 1;
338			cs[-1] = ' ';
339		}
340	}
341	if (verbose > 0)
342		jail_note(j, "run command%s%s%s: %s\n",
343		    injail ? " in jail" : "", username ? " as " : "",
344		    username ? username : "", j->comline);
345
346	pid = fork();
347	if (pid < 0)
348		err(1, "fork");
349	if (pid > 0) {
350		if (bg) {
351			j->flags |= JF_BACKGROUND;
352			requeue(j, &ready);
353		} else {
354			--*plimit;
355			add_proc(j, pid);
356		}
357		return 1;
358	}
359	if (bg)
360		setsid();
361
362	pwd = NULL;
363	lcap = NULL;
364	if ((clean || username) && injail && sjuser &&
365	    get_user_info(j, username, &pwd, &lcap) < 0)
366		exit(1);
367	if (injail) {
368		/* jail_attach won't chdir along with its chroot. */
369		path = string_param(j->intparams[KP_PATH]);
370		if (path && chdir(path) < 0) {
371			jail_warnx(j, "chdir %s: %s", path, strerror(errno));
372			exit(1);
373		}
374		if (int_param(j->intparams[IP_EXEC_FIB], &fib) &&
375		    setfib(fib) < 0) {
376			jail_warnx(j, "setfib: %s", strerror(errno));
377			exit(1);
378		}
379		if (jail_attach(j->jid) < 0) {
380			jail_warnx(j, "jail_attach: %s", strerror(errno));
381			exit(1);
382		}
383	}
384	if (clean || username) {
385		if (!(injail && sjuser) &&
386		    get_user_info(j, username, &pwd, &lcap) < 0)
387			exit(1);
388		if (clean) {
389			term = getenv("TERM");
390			environ = &cleanenv;
391			setenv("PATH", "/bin:/usr/bin", 0);
392			setenv("TERM", term, 1);
393		}
394		if (setusercontext(lcap, pwd, pwd->pw_uid, username
395		    ? LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN
396		    : LOGIN_SETPATH | LOGIN_SETENV) < 0) {
397			jail_warnx(j, "setusercontext %s: %s", pwd->pw_name,
398			    strerror(errno));
399			exit(1);
400		}
401		login_close(lcap);
402		setenv("USER", pwd->pw_name, 1);
403		setenv("HOME", pwd->pw_dir, 1);
404		setenv("SHELL",
405		    *pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL, 1);
406		if (clean && chdir(pwd->pw_dir) < 0) {
407			jail_warnx(j, "chdir %s: %s",
408			    pwd->pw_dir, strerror(errno));
409			exit(1);
410		}
411		endpwent();
412	}
413
414	if (consfd != 0 && (dup2(consfd, 1) < 0 || dup2(consfd, 2) < 0)) {
415		jail_warnx(j, "exec.consolelog: %s", strerror(errno));
416		exit(1);
417	}
418	closefrom(3);
419	execvp(argv[0], argv);
420	jail_warnx(j, "exec %s: %s", argv[0], strerror(errno));
421	exit(1);
422}
423
424/*
425 * Check command exit status
426 */
427int
428finish_command(struct cfjail *j, int *plimit)
429{
430	int error;
431
432	if (j->flags & (JF_RUNQ | JF_BACKGROUND))
433		return 0;
434	++*plimit;
435	if (!TAILQ_EMPTY(&runnable))
436		requeue(TAILQ_FIRST(&runnable), &ready);
437	error = 0;
438	if (j->flags & JF_TIMEOUT) {
439		j->flags &= ~JF_TIMEOUT;
440		if (j->comparam != IP_STOP_TIMEOUT) {
441			jail_warnx(j, "%s: timed out", j->comline);
442			failed(j);
443			error = -1;
444		} else if (verbose > 0)
445			jail_note(j, "timed out\n");
446	} else if (j->pstatus != 0) {
447		if (WIFSIGNALED(j->pstatus))
448			jail_warnx(j, "%s: exited on signal %d",
449			    j->comline, WTERMSIG(j->pstatus));
450		else
451			jail_warnx(j, "%s: failed", j->comline);
452		failed(j);
453		error = -1;
454	}
455	free(j->comline);
456	return error;
457}
458
459/*
460 * Check for finished processed or timeouts.
461 */
462struct cfjail *
463next_proc(int nonblock)
464{
465	struct kevent ke;
466	struct timespec ts;
467	struct timespec *tsp;
468	struct cfjail *j;
469
470	if (!TAILQ_EMPTY(&sleeping)) {
471	again:
472		tsp = NULL;
473		if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) {
474			clock_gettime(CLOCK_REALTIME, &ts);
475			ts.tv_sec = j->timeout.tv_sec - ts.tv_sec;
476			ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec;
477			if (ts.tv_nsec < 0) {
478				ts.tv_sec--;
479				ts.tv_nsec += 1000000000;
480			}
481			if (ts.tv_sec < 0 ||
482			    (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
483				j->flags |= JF_TIMEOUT;
484				clear_procs(j);
485				return j;
486			}
487			tsp = &ts;
488		}
489		if (nonblock) {
490			ts.tv_sec = 0;
491			ts.tv_nsec = 0;
492			tsp = &ts;
493		}
494		switch (kevent(kq, NULL, 0, &ke, 1, tsp)) {
495		case -1:
496			if (errno != EINTR)
497				err(1, "kevent");
498			goto again;
499		case 0:
500			if (!nonblock) {
501				j = TAILQ_FIRST(&sleeping);
502				j->flags |= JF_TIMEOUT;
503				clear_procs(j);
504				return j;
505			}
506			break;
507		case 1:
508			(void)waitpid(ke.ident, NULL, WNOHANG);
509			if ((j = find_proc(ke.ident))) {
510				j->pstatus = ke.data;
511				return j;
512			}
513			goto again;
514		}
515	}
516	return NULL;
517}
518
519/*
520 * Send SIGTERM to all processes in a jail and wait for them to die.
521 */
522int
523term_procs(struct cfjail *j)
524{
525	struct kinfo_proc *ki;
526	int i, noted, pcnt, timeout;
527
528	static kvm_t *kd;
529
530	if (!int_param(j->intparams[IP_STOP_TIMEOUT], &timeout))
531		timeout = DEFAULT_STOP_TIMEOUT;
532	else if (timeout == 0)
533		return 0;
534
535	if (kd == NULL) {
536		kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "jail");
537		if (kd == NULL)
538			exit(1);
539	}
540
541	ki = kvm_getprocs(kd, KERN_PROC_PROC, 0, &pcnt);
542	if (ki == NULL)
543		exit(1);
544	noted = 0;
545	for (i = 0; i < pcnt; i++)
546		if (ki[i].ki_jid == j->jid &&
547		    kill(ki[i].ki_pid, SIGTERM) == 0) {
548			add_proc(j, ki[i].ki_pid);
549			if (verbose > 0) {
550				if (!noted) {
551					noted = 1;
552					jail_note(j, "sent SIGTERM to:");
553				}
554				printf(" %d", ki[i].ki_pid);
555			}
556		}
557	if (noted)
558		printf("\n");
559	if (j->nprocs > 0) {
560		clock_gettime(CLOCK_REALTIME, &j->timeout);
561		j->timeout.tv_sec += timeout;
562		return 1;
563	}
564	return 0;
565}
566
567/*
568 * Add a process to the hash, tied to a jail.
569 */
570static void
571add_proc(struct cfjail *j, pid_t pid)
572{
573	struct kevent ke;
574	struct cfjail *tj;
575	struct phash *ph;
576
577	if (!kq && (kq = kqueue()) < 0)
578		err(1, "kqueue");
579	EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
580	if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)
581		err(1, "kevent");
582	ph = emalloc(sizeof(struct phash));
583	ph->j = j;
584	ph->pid = pid;
585	LIST_INSERT_HEAD(&phash[pid % PHASH_SIZE], ph, le);
586	j->nprocs++;
587	if (j->timeout.tv_sec) {
588		TAILQ_REMOVE(j->queue, j, tq);
589		TAILQ_FOREACH(tj, &sleeping, tq) {
590			if (!tj->timeout.tv_sec ||
591			    j->timeout.tv_sec < tj->timeout.tv_sec ||
592			    (j->timeout.tv_sec == tj->timeout.tv_sec &&
593			    j->timeout.tv_nsec <= tj->timeout.tv_nsec)) {
594				TAILQ_INSERT_BEFORE(tj, j, tq);
595				break;
596			}
597		}
598		if (tj == NULL)
599			TAILQ_INSERT_TAIL(&sleeping, j, tq);
600		j->queue = &sleeping;
601	} else
602		requeue(j, &sleeping);
603}
604
605/*
606 * Remove any processes from the hash that correspond to a jail.
607 */
608static void
609clear_procs(struct cfjail *j)
610{
611	struct kevent ke;
612	struct phash *ph, *tph;
613	int i;
614
615	j->nprocs = 0;
616	for (i = 0; i < PHASH_SIZE; i++)
617		LIST_FOREACH_SAFE(ph, &phash[i], le, tph)
618			if (ph->j == j) {
619				EV_SET(&ke, ph->pid, EVFILT_PROC, EV_DELETE,
620				    NOTE_EXIT, 0, NULL);
621				(void)kevent(kq, &ke, 1, NULL, 0, NULL);
622				LIST_REMOVE(ph, le);
623				free(ph);
624			}
625}
626
627/*
628 * Find the jail that corresponds to an exited process.
629 */
630static struct cfjail *
631find_proc(pid_t pid)
632{
633	struct cfjail *j;
634	struct phash *ph;
635
636	LIST_FOREACH(ph, &phash[pid % PHASH_SIZE], le)
637		if (ph->pid == pid) {
638			j = ph->j;
639			LIST_REMOVE(ph, le);
640			free(ph);
641			return --j->nprocs ? NULL : j;
642		}
643	return NULL;
644}
645
646/*
647 * Look up a user in the passwd and login.conf files.
648 */
649static int
650get_user_info(struct cfjail *j, const char *username,
651    const struct passwd **pwdp, login_cap_t **lcapp)
652{
653	const struct passwd *pwd;
654
655	*pwdp = pwd = username ? getpwnam(username) : getpwuid(getuid());
656	if (pwd == NULL) {
657		if (errno)
658			jail_warnx(j, "getpwnam%s%s: %s", username ? " " : "",
659			    username ? username : "", strerror(errno));
660		else if (username)
661			jail_warnx(j, "%s: no such user", username);
662		else
663			jail_warnx(j, "unknown uid %d", getuid());
664		return -1;
665	}
666	*lcapp = login_getpwclass(pwd);
667	if (*lcapp == NULL) {
668		jail_warnx(j, "getpwclass %s: %s", pwd->pw_name,
669		    strerror(errno));
670		return -1;
671	}
672	/* Set the groups while the group file is still available */
673	if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) {
674		jail_warnx(j, "initgroups %s: %s", pwd->pw_name,
675		    strerror(errno));
676		return -1;
677	}
678	return 0;
679}
680
681/*
682 * Make sure a mount or consolelog path is a valid absolute pathname
683 * with no symlinks.
684 */
685static int
686check_path(struct cfjail *j, const char *pname, const char *path, int isfile)
687{
688	struct stat st;
689	char *tpath, *p;
690	const char *jailpath;
691	size_t jplen;
692
693	if (path[0] != '/') {
694		jail_warnx(j, "%s: %s: not an absolute pathname",
695		    pname, path);
696		failed(j);
697		return -1;
698	}
699	/*
700	 * Only check for symlinks in components below the jail's path,
701	 * since that's where the security risk lies.
702	 */
703	jailpath = string_param(j->intparams[KP_PATH]);
704	if (jailpath == NULL)
705		jailpath = "";
706	jplen = strlen(jailpath);
707	if (strncmp(path, jailpath, jplen) || path[jplen] != '/')
708		return 0;
709	tpath = alloca(strlen(path) + 1);
710	strcpy(tpath, path);
711	for (p = tpath + jplen; p != NULL; ) {
712		p = strchr(p + 1, '/');
713		if (p)
714			*p = '\0';
715		if (lstat(tpath, &st) < 0) {
716			if (errno == ENOENT && isfile && !p)
717				break;
718			jail_warnx(j, "%s: %s: %s", pname, tpath,
719			    strerror(errno));
720			failed(j);
721			return -1;
722		}
723		if (S_ISLNK(st.st_mode)) {
724			jail_warnx(j, "%s: %s is a symbolic link",
725			    pname, tpath);
726			failed(j);
727			return -1;
728		}
729		if (p)
730			*p = '/';
731	}
732	return 0;
733}
734