jail.c revision 192896
1191668Sjamie/*-
2191668Sjamie * Copyright (c) 1999 Poul-Henning Kamp.
3192896Sjamie * Copyright (c) 2009 James Gritton
4191668Sjamie * All rights reserved.
5191668Sjamie *
6191668Sjamie * Redistribution and use in source and binary forms, with or without
7191668Sjamie * modification, are permitted provided that the following conditions
8191668Sjamie * are met:
9191668Sjamie * 1. Redistributions of source code must retain the above copyright
10191668Sjamie *    notice, this list of conditions and the following disclaimer.
11191668Sjamie * 2. Redistributions in binary form must reproduce the above copyright
12191668Sjamie *    notice, this list of conditions and the following disclaimer in the
13191668Sjamie *    documentation and/or other materials provided with the distribution.
14191668Sjamie *
15191668Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16191668Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17191668Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18191668Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19191668Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20191668Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21191668Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22191668Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23191668Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24191668Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25191668Sjamie * SUCH DAMAGE.
2646432Sphk */
2746432Sphk
28117280Scharnier#include <sys/cdefs.h>
29117280Scharnier__FBSDID("$FreeBSD: head/usr.sbin/jail/jail.c 192896 2009-05-27 14:30:26Z jamie $");
30117280Scharnier
31112705Smaxim#include <sys/param.h>
3246155Sphk#include <sys/jail.h>
33185435Sbz#include <sys/socket.h>
34158428Smatteo#include <sys/sysctl.h>
35192896Sjamie#include <sys/uio.h>
3678723Sdd
37192896Sjamie#include <arpa/inet.h>
3846155Sphk#include <netinet/in.h>
3946155Sphk
40192896Sjamie#include <ctype.h>
4178723Sdd#include <err.h>
42129848Smaxim#include <errno.h>
43112705Smaxim#include <grp.h>
44112705Smaxim#include <login_cap.h>
45192896Sjamie#include <netdb.h>
46133743Smaxim#include <paths.h>
47112705Smaxim#include <pwd.h>
4878723Sdd#include <stdio.h>
4978723Sdd#include <stdlib.h>
5078723Sdd#include <string.h>
5178723Sdd#include <unistd.h>
5278723Sdd
53192896Sjamie#define	SJPARAM		"security.jail.param"
54192896Sjamie#define	ERRMSG_SIZE	256
55192896Sjamie
56192896Sjamiestruct param {
57192896Sjamie	struct iovec name;
58192896Sjamie	struct iovec value;
59192896Sjamie};
60192896Sjamie
61192896Sjamiestatic struct param *params;
62192896Sjamiestatic char **param_values;
63192896Sjamiestatic int nparams;
64192896Sjamie
65192896Sjamiestatic char *ip4_addr;
66185435Sbz#ifdef INET6
67192896Sjamiestatic char *ip6_addr;
68185435Sbz#endif
69185435Sbz
70192896Sjamiestatic void add_ip_addr(char **addrp, char *newaddr);
71185435Sbz#ifdef INET6
72192896Sjamiestatic void add_ip_addr46(char *newaddr);
73185435Sbz#endif
74192896Sjamiestatic void add_ip_addrinfo(int ai_flags, char *value);
75192896Sjamiestatic void quoted_print(FILE *fp, char *str);
76192896Sjamiestatic void set_param(const char *name, char *value);
77192896Sjamiestatic void usage(void);
78185435Sbz
79192896Sjamieextern char **environ;
80192896Sjamie
81129848Smaxim#define GET_USER_INFO do {						\
82129848Smaxim	pwd = getpwnam(username);					\
83129848Smaxim	if (pwd == NULL) {						\
84129848Smaxim		if (errno)						\
85129848Smaxim			err(1, "getpwnam: %s", username);		\
86129848Smaxim		else							\
87129848Smaxim			errx(1, "%s: no such user", username);		\
88129848Smaxim	}								\
89129848Smaxim	lcap = login_getpwclass(pwd);					\
90129848Smaxim	if (lcap == NULL)						\
91129848Smaxim		err(1, "getpwclass: %s", username);			\
92129848Smaxim	ngroups = NGROUPS;						\
93129848Smaxim	if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0)	\
94129848Smaxim		err(1, "getgrouplist: %s", username);			\
95129848Smaxim} while (0)
96129848Smaxim
9746155Sphkint
9846155Sphkmain(int argc, char **argv)
9946155Sphk{
100137808Sdelphij	login_cap_t *lcap = NULL;
101192896Sjamie	struct iovec rparams[2];
102137808Sdelphij	struct passwd *pwd = NULL;
103136051Sstefanf	gid_t groups[NGROUPS];
104192896Sjamie	int ch, cmdarg, i, jail_set_flags, jid, ngroups;
105192896Sjamie	int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag;
106192896Sjamie	char *ep, *jailname, *securelevel, *username, *JidFile;
107192896Sjamie	char errmsg[ERRMSG_SIZE];
108133743Smaxim	static char *cleanenv;
109137807Sdelphij	const char *shell, *p = NULL;
110153056Sphilip	FILE *fp;
11146155Sphk
112192896Sjamie	hflag = iflag = Jflag = lflag = rflag = uflag = Uflag =
113192896Sjamie	    jail_set_flags = 0;
114192896Sjamie	cmdarg = jid = -1;
115192896Sjamie	jailname = securelevel = username = JidFile = cleanenv = NULL;
116153056Sphilip	fp = NULL;
117112705Smaxim
118192896Sjamie	while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) {
119112705Smaxim		switch (ch) {
120192896Sjamie		case 'd':
121192896Sjamie			jail_set_flags |= JAIL_DYING;
122192896Sjamie			break;
123185435Sbz		case 'h':
124185435Sbz			hflag = 1;
125185435Sbz			break;
126113277Smike		case 'i':
127113277Smike			iflag = 1;
128113277Smike			break;
129153056Sphilip		case 'J':
130153056Sphilip			JidFile = optarg;
131153056Sphilip			Jflag = 1;
132153056Sphilip			break;
133185435Sbz		case 'n':
134185435Sbz			jailname = optarg;
135185435Sbz			break;
136158428Smatteo		case 's':
137192896Sjamie			securelevel = optarg;
138158428Smatteo			break;
139112705Smaxim		case 'u':
140112705Smaxim			username = optarg;
141129848Smaxim			uflag = 1;
142112705Smaxim			break;
143129848Smaxim		case 'U':
144129848Smaxim			username = optarg;
145129848Smaxim			Uflag = 1;
146129848Smaxim			break;
147133743Smaxim		case 'l':
148133743Smaxim			lflag = 1;
149133743Smaxim			break;
150192896Sjamie		case 'c':
151192896Sjamie			jail_set_flags |= JAIL_CREATE;
152192896Sjamie			break;
153192896Sjamie		case 'm':
154192896Sjamie			jail_set_flags |= JAIL_UPDATE;
155192896Sjamie			break;
156192896Sjamie		case 'r':
157192896Sjamie			jid = strtoul(optarg, &ep, 10);
158192896Sjamie			if (!*optarg || *ep) {
159192896Sjamie				*(const void **)&rparams[0].iov_base = "name";
160192896Sjamie				rparams[0].iov_len = sizeof("name");
161192896Sjamie				rparams[1].iov_base = optarg;
162192896Sjamie				rparams[1].iov_len = strlen(optarg) + 1;
163192896Sjamie				jid = jail_get(rparams, 2, 0);
164192896Sjamie				if (jid < 0)
165192896Sjamie					errx(1, "unknown jail: %s", optarg);
166192896Sjamie			}
167192896Sjamie			rflag = 1;
168192896Sjamie			break;
169112705Smaxim		default:
170112705Smaxim			usage();
171112705Smaxim		}
172113277Smike	}
173112705Smaxim	argc -= optind;
174112705Smaxim	argv += optind;
175192896Sjamie	if (rflag) {
176192896Sjamie		if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag)
177192896Sjamie			usage();
178192896Sjamie		if (jail_remove(jid) < 0)
179192896Sjamie			err(1, "jail_remove");
180192896Sjamie		exit (0);
181192896Sjamie	}
182192896Sjamie	if (argc == 0)
183112705Smaxim		usage();
184129848Smaxim	if (uflag && Uflag)
185129848Smaxim		usage();
186133743Smaxim	if (lflag && username == NULL)
187133743Smaxim		usage();
188129848Smaxim	if (uflag)
189129848Smaxim		GET_USER_INFO;
190185435Sbz
191192896Sjamie	/*
192192896Sjamie	 * If the first argument (path) starts with a slash, and the third
193192896Sjamie	 * argument (IP address) starts with a digit, it is likely to be
194192896Sjamie	 * an old-style fixed-parameter command line.
195192896Sjamie	 */
196192896Sjamie	if (jailname)
197192896Sjamie		set_param("name", jailname);
198192896Sjamie	if (securelevel)
199192896Sjamie		set_param("securelevel", securelevel);
200192896Sjamie	if (jail_set_flags) {
201192896Sjamie		for (i = 0; i < argc; i++) {
202192896Sjamie			if (!strncmp(argv[i], "command=", 8)) {
203192896Sjamie				cmdarg = i;
204192896Sjamie				argv[cmdarg] += 8;
205192896Sjamie				jail_set_flags |= JAIL_ATTACH;
206192896Sjamie				break;
207192896Sjamie			}
208192896Sjamie			if (hflag) {
209192896Sjamie				if (!strncmp(argv[i], "ip4.addr=", 9)) {
210192896Sjamie					add_ip_addr(&ip4_addr, argv[i] + 9);
211192896Sjamie					break;
212192896Sjamie				}
213192896Sjamie#ifdef INET6
214192896Sjamie				if (!strncmp(argv[i], "ip6.addr=", 9)) {
215192896Sjamie					add_ip_addr(&ip6_addr, argv[i] + 9);
216192896Sjamie					break;
217192896Sjamie				}
218192896Sjamie#endif
219192896Sjamie				if (!strncmp(argv[i], "host.hostname=", 14))
220192896Sjamie					add_ip_addrinfo(0, argv[i] + 14);
221192896Sjamie			}
222192896Sjamie			set_param(NULL, argv[i]);
223192896Sjamie		}
224192896Sjamie	} else {
225192896Sjamie		if (argc < 4 || argv[0][0] != '/')
226192896Sjamie			errx(1, "%s\n%s",
227192896Sjamie			   "no -c or -m, so this must be an old-style command.",
228192896Sjamie			   "But it doesn't look like one.");
229192896Sjamie		set_param("path", argv[0]);
230192896Sjamie		set_param("host.hostname", argv[1]);
231192896Sjamie		if (hflag)
232192896Sjamie			add_ip_addrinfo(0, argv[1]);
233192896Sjamie#ifdef INET6
234192896Sjamie		add_ip_addr46(argv[2]);
235192896Sjamie#else
236192896Sjamie		add_ip_addr(&ip4_addr, argv[2]);
237192896Sjamie#endif
238192896Sjamie		cmdarg = 3;
239185435Sbz	}
240192896Sjamie	if (ip4_addr != NULL)
241192896Sjamie		set_param("ip4.addr", ip4_addr);
242185435Sbz#ifdef INET6
243192896Sjamie	if (ip6_addr != NULL)
244192896Sjamie		set_param("ip6.addr", ip6_addr);
245192896Sjamie#endif
246192896Sjamie	errmsg[0] = 0;
247192896Sjamie	set_param("errmsg", errmsg);
248185435Sbz
249153056Sphilip	if (Jflag) {
250153056Sphilip		fp = fopen(JidFile, "w");
251153056Sphilip		if (fp == NULL)
252153056Sphilip			errx(1, "Could not create JidFile: %s", JidFile);
253153056Sphilip	}
254192896Sjamie	jid = jail_set(&params->name, 2 * nparams,
255192896Sjamie	    jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH);
256192896Sjamie	if (jid < 0) {
257192896Sjamie		if (errmsg[0] != '\0')
258192896Sjamie			errx(1, "%s", errmsg);
259192896Sjamie		err(1, "jail_set");
260192896Sjamie	}
261113804Smike	if (iflag) {
262192896Sjamie		printf("%d\n", jid);
263113804Smike		fflush(stdout);
264113804Smike	}
265153056Sphilip	if (Jflag) {
266192896Sjamie		if (jail_set_flags) {
267192896Sjamie			fprintf(fp, "jid=%d", jid);
268192896Sjamie			for (i = 0; i < nparams; i++)
269192896Sjamie				if (strcmp(params[i].name.iov_base, "jid") &&
270192896Sjamie				    strcmp(params[i].name.iov_base, "errmsg")) {
271192896Sjamie					fprintf(fp, " %s",
272192896Sjamie					    (char *)params[i].name.iov_base);
273192896Sjamie					if (param_values[i]) {
274192896Sjamie						putc('=', fp);
275192896Sjamie						quoted_print(fp,
276192896Sjamie						    param_values[i]);
277192896Sjamie					}
278192896Sjamie				}
279192896Sjamie			fprintf(fp, "\n");
280192896Sjamie		} else {
281192896Sjamie			for (i = 0; i < nparams; i++)
282192896Sjamie				if (!strcmp(params[i].name.iov_base, "path"))
283192896Sjamie					break;
284192896Sjamie#ifdef INET6
285192896Sjamie			fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n",
286192896Sjamie			    jid, i < nparams
287192896Sjamie			    ? (char *)params[i].value.iov_base : argv[0],
288192896Sjamie			    argv[1], ip4_addr ? ip4_addr : "",
289192896Sjamie			    ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0]
290192896Sjamie			    ? "," : "", ip6_addr ? ip6_addr : "", argv[3]);
291192896Sjamie#else
292153056Sphilip			fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
293192896Sjamie			    jid, i < nparams
294192896Sjamie			    ? (char *)params[i].value.iov_base : argv[0],
295192896Sjamie			    argv[1], ip4_addr ? ip4_addr : "", argv[3]);
296192896Sjamie#endif
297153056Sphilip		}
298192896Sjamie		(void)fclose(fp);
299153056Sphilip	}
300192896Sjamie	if (cmdarg < 0)
301192896Sjamie		exit(0);
302112705Smaxim	if (username != NULL) {
303129848Smaxim		if (Uflag)
304129848Smaxim			GET_USER_INFO;
305133743Smaxim		if (lflag) {
306133743Smaxim			p = getenv("TERM");
307133743Smaxim			environ = &cleanenv;
308133743Smaxim		}
309112972Smaxim		if (setgroups(ngroups, groups) != 0)
310112972Smaxim			err(1, "setgroups");
311112972Smaxim		if (setgid(pwd->pw_gid) != 0)
312112972Smaxim			err(1, "setgid");
313112972Smaxim		if (setusercontext(lcap, pwd, pwd->pw_uid,
314157790Smaxim		    LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
315112972Smaxim			err(1, "setusercontext");
316113206Smaxim		login_close(lcap);
317112705Smaxim	}
318133743Smaxim	if (lflag) {
319133743Smaxim		if (*pwd->pw_shell)
320133743Smaxim			shell = pwd->pw_shell;
321133743Smaxim		else
322133743Smaxim			shell = _PATH_BSHELL;
323133743Smaxim		if (chdir(pwd->pw_dir) < 0)
324133743Smaxim			errx(1, "no home directory");
325133743Smaxim		setenv("HOME", pwd->pw_dir, 1);
326133743Smaxim		setenv("SHELL", shell, 1);
327133743Smaxim		setenv("USER", pwd->pw_name, 1);
328133743Smaxim		if (p)
329133743Smaxim			setenv("TERM", p, 1);
330133743Smaxim	}
331192896Sjamie	execvp(argv[cmdarg], argv + cmdarg);
332192896Sjamie	err(1, "execvp: %s", argv[cmdarg]);
33346155Sphk}
334112705Smaxim
335112705Smaximstatic void
336192896Sjamieadd_ip_addr(char **addrp, char *value)
337112705Smaxim{
338192896Sjamie	int addrlen;
339192896Sjamie	char *addr;
340112705Smaxim
341192896Sjamie	if (!*addrp) {
342192896Sjamie		*addrp = strdup(value);
343192896Sjamie		if (!*addrp)
344192896Sjamie			err(1, "malloc");
345192896Sjamie	} else if (value[0]) {
346192896Sjamie		addrlen = strlen(*addrp) + strlen(value) + 2;
347192896Sjamie		addr = malloc(addrlen);
348192896Sjamie		if (!addr)
349192896Sjamie			err(1, "malloc");
350192896Sjamie		snprintf(addr, addrlen, "%s,%s", *addrp, value);
351192896Sjamie		free(*addrp);
352192896Sjamie		*addrp = addr;
353192896Sjamie	}
354112705Smaxim}
355185435Sbz
356192896Sjamie#ifdef INET6
357192896Sjamiestatic void
358192896Sjamieadd_ip_addr46(char *value)
359185435Sbz{
360192896Sjamie	char *p, *np;
361192896Sjamie
362192896Sjamie	if (!value[0]) {
363192896Sjamie		add_ip_addr(&ip4_addr, value);
364192896Sjamie		add_ip_addr(&ip6_addr, value);
365192896Sjamie		return;
366192896Sjamie	}
367192896Sjamie	for (p = value;; p = np + 1)
368192896Sjamie	{
369192896Sjamie		np = strchr(p, ',');
370192896Sjamie		if (np)
371192896Sjamie			*np = '\0';
372192896Sjamie		add_ip_addrinfo(AI_NUMERICHOST, p);
373192896Sjamie		if (!np)
374192896Sjamie			break;
375192896Sjamie	}
376192896Sjamie}
377192896Sjamie#endif
378192896Sjamie
379192896Sjamiestatic void
380192896Sjamieadd_ip_addrinfo(int ai_flags, char *value)
381192896Sjamie{
382192896Sjamie	struct addrinfo hints, *ai0, *ai;
383192896Sjamie	struct in_addr addr4;
384185435Sbz	int error;
385192896Sjamie	char avalue4[INET_ADDRSTRLEN];
386185435Sbz#ifdef INET6
387192896Sjamie	struct in6_addr addr6;
388192896Sjamie	char avalue6[INET6_ADDRSTRLEN];
389185435Sbz#endif
390185435Sbz
391192896Sjamie	/* Look up the hostname (or get the address) */
392192896Sjamie	memset(&hints, 0, sizeof(hints));
393192896Sjamie	hints.ai_socktype = SOCK_STREAM;
394192896Sjamie#ifdef INET6
395192896Sjamie	hints.ai_family = PF_UNSPEC;
396192896Sjamie#else
397192896Sjamie	hints.ai_family = PF_INET;
398192896Sjamie#endif
399192896Sjamie	hints.ai_flags = ai_flags;
400192896Sjamie	error = getaddrinfo(value, NULL, &hints, &ai0);
401192896Sjamie	if (error != 0)
402192896Sjamie		errx(1, "hostname %s: %s", value, gai_strerror(error));
403192896Sjamie
404192896Sjamie	/* Convert the addresses to ASCII so set_param can convert them back. */
405192896Sjamie	for (ai = ai0; ai; ai = ai->ai_next)
406192896Sjamie		switch (ai->ai_family) {
407185435Sbz		case AF_INET:
408192896Sjamie			memcpy(&addr4, &((struct sockaddr_in *)
409192896Sjamie			    (void *)ai->ai_addr)->sin_addr, sizeof(addr4));
410192896Sjamie			if (inet_ntop(AF_INET, &addr4, avalue4,
411192896Sjamie			    INET_ADDRSTRLEN) == NULL)
412192896Sjamie				err(1, "inet_ntop");
413192896Sjamie			add_ip_addr(&ip4_addr, avalue4);
414185435Sbz			break;
415185435Sbz#ifdef INET6
416185435Sbz		case AF_INET6:
417192896Sjamie			memcpy(&addr6, &((struct sockaddr_in6 *)
418192896Sjamie			    (void *)ai->ai_addr)->sin6_addr, sizeof(addr6));
419192896Sjamie			if (inet_ntop(AF_INET6, &addr6, avalue6,
420192896Sjamie			    INET6_ADDRSTRLEN) == NULL)
421192896Sjamie				err(1, "inet_ntop");
422192896Sjamie			add_ip_addr(&ip6_addr, avalue6);
423185435Sbz			break;
424185435Sbz#endif
425185435Sbz		}
426192896Sjamie	freeaddrinfo(ai0);
427185435Sbz}
428185435Sbz
429192896Sjamiestatic void
430192896Sjamiequoted_print(FILE *fp, char *str)
431185435Sbz{
432192896Sjamie	int c, qc;
433192896Sjamie	char *p = str;
434185435Sbz
435192896Sjamie	/* An empty string needs quoting. */
436192896Sjamie	if (!*p) {
437192896Sjamie		fputs("\"\"", fp);
438192896Sjamie		return;
439192896Sjamie	}
440185435Sbz
441192896Sjamie	/*
442192896Sjamie	 * The value will be surrounded by quotes if it contains spaces
443192896Sjamie	 * or quotes.
444192896Sjamie	 */
445192896Sjamie	qc = strchr(p, '\'') ? '"'
446192896Sjamie	    : strchr(p, '"') ? '\''
447192896Sjamie	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
448192896Sjamie	    : 0;
449192896Sjamie	if (qc)
450192896Sjamie		putc(qc, fp);
451192896Sjamie	while ((c = *p++)) {
452192896Sjamie		if (c == '\\' || c == qc)
453192896Sjamie			putc('\\', fp);
454192896Sjamie		putc(c, fp);
455185435Sbz	}
456192896Sjamie	if (qc)
457192896Sjamie		putc(qc, fp);
458185435Sbz}
459185435Sbz
460192896Sjamiestatic void
461192896Sjamieset_param(const char *name, char *value)
462185435Sbz{
463192896Sjamie	struct param *param;
464192896Sjamie	char *ep, *p;
465192896Sjamie	size_t buflen, mlen;
466192896Sjamie	int i, nval, mib[CTL_MAXNAME];
467192896Sjamie	struct {
468192896Sjamie		int i;
469192896Sjamie		char s[MAXPATHLEN];
470192896Sjamie	} buf;
471185435Sbz
472192896Sjamie	static int paramlistsize;
473185435Sbz
474192896Sjamie	/* Separate the name from the value, if not done already. */
475192896Sjamie	if (name == NULL) {
476192896Sjamie		name = value;
477192896Sjamie		if ((value = strchr(value, '=')))
478192896Sjamie			*value++ = '\0';
479192896Sjamie	}
480185435Sbz
481192896Sjamie	/* Check for repeat parameters */
482192896Sjamie	for (i = 0; i < nparams; i++)
483192896Sjamie		if (!strcmp(name, params[i].name.iov_base)) {
484192896Sjamie			memcpy(params + i, params + i + 1,
485192896Sjamie			    (--nparams - i) * sizeof(struct param));
486192896Sjamie			break;
487192896Sjamie		}
488185435Sbz
489192896Sjamie	/* Make sure there is room for the new param record. */
490192896Sjamie	if (!nparams) {
491192896Sjamie		paramlistsize = 32;
492192896Sjamie		params = malloc(paramlistsize * sizeof(*params));
493192896Sjamie		param_values = malloc(paramlistsize * sizeof(*param_values));
494192896Sjamie		if (params == NULL || param_values == NULL)
495192896Sjamie			err(1, "malloc");
496192896Sjamie	} else if (nparams >= paramlistsize) {
497192896Sjamie		paramlistsize *= 2;
498192896Sjamie		params = realloc(params, paramlistsize * sizeof(*params));
499192896Sjamie		param_values = realloc(param_values,
500192896Sjamie		    paramlistsize * sizeof(*param_values));
501192896Sjamie		if (params == NULL)
502192896Sjamie			err(1, "realloc");
503192896Sjamie	}
504185435Sbz
505192896Sjamie	/* Look up the paramter. */
506192896Sjamie	param_values[nparams] = value;
507192896Sjamie	param = params + nparams++;
508192896Sjamie	*(const void **)&param->name.iov_base = name;
509192896Sjamie	param->name.iov_len = strlen(name) + 1;
510192896Sjamie	/* Trivial values - no value or errmsg. */
511192896Sjamie	if (value == NULL) {
512192896Sjamie		param->value.iov_base = NULL;
513192896Sjamie		param->value.iov_len = 0;
514192896Sjamie		return;
515185435Sbz	}
516192896Sjamie	if (!strcmp(name, "errmsg")) {
517192896Sjamie		param->value.iov_base = value;
518192896Sjamie		param->value.iov_len = ERRMSG_SIZE;
519192896Sjamie		return;
520192896Sjamie	}
521192896Sjamie	mib[0] = 0;
522192896Sjamie	mib[1] = 3;
523192896Sjamie	snprintf(buf.s, sizeof(buf.s), SJPARAM ".%s", name);
524192896Sjamie	mlen = sizeof(mib) - 2 * sizeof(int);
525192896Sjamie	if (sysctl(mib, 2, mib + 2, &mlen, buf.s, strlen(buf.s)) < 0)
526192896Sjamie		errx(1, "unknown parameter: %s", name);
527192896Sjamie	mib[1] = 4;
528192896Sjamie	buflen = sizeof(buf);
529192896Sjamie	if (sysctl(mib, (mlen / sizeof(int)) + 2, &buf, &buflen, NULL, 0) < 0)
530192896Sjamie		err(1, "sysctl(0.4.%s)", name);
531192896Sjamie	/*
532192896Sjamie	 * See if this is an array type.
533192896Sjamie	 * Treat non-arrays as an array of one.
534192896Sjamie	 */
535192896Sjamie	p = strchr(buf.s, '\0');
536192896Sjamie	nval = 1;
537192896Sjamie	if (p - 2 >= buf.s && !strcmp(p - 2, ",a")) {
538192896Sjamie		if (value[0] == '\0' ||
539192896Sjamie		    (value[0] == '-' && value[1] == '\0')) {
540192896Sjamie			param->value.iov_base = value;
541192896Sjamie			param->value.iov_len = 0;
542192896Sjamie			return;
543192896Sjamie		}
544192896Sjamie		p[-2] = 0;
545192896Sjamie		for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) {
546192896Sjamie			*p = '\0';
547192896Sjamie			nval++;
548192896Sjamie		}
549192896Sjamie	}
550192896Sjamie
551192896Sjamie	/* Set the values according to the parameter type. */
552192896Sjamie	switch (buf.i & CTLTYPE) {
553192896Sjamie	case CTLTYPE_INT:
554192896Sjamie	case CTLTYPE_UINT:
555192896Sjamie		param->value.iov_len = nval * sizeof(int);
556192896Sjamie		break;
557192896Sjamie	case CTLTYPE_LONG:
558192896Sjamie	case CTLTYPE_ULONG:
559192896Sjamie		param->value.iov_len = nval * sizeof(long);
560192896Sjamie		break;
561192896Sjamie	case CTLTYPE_STRUCT:
562192896Sjamie		if (!strcmp(buf.s, "S,in_addr"))
563192896Sjamie			param->value.iov_len = nval * sizeof(struct in_addr);
564192896Sjamie#ifdef INET6
565192896Sjamie		else if (!strcmp(buf.s, "S,in6_addr"))
566192896Sjamie			param->value.iov_len = nval * sizeof(struct in6_addr);
567192896Sjamie#endif
568192896Sjamie		else
569192896Sjamie			errx(1, "%s: unknown parameter structure (%s)",
570192896Sjamie			    name, buf.s);
571192896Sjamie		break;
572192896Sjamie	case CTLTYPE_STRING:
573192896Sjamie		if (!strcmp(name, "path")) {
574192896Sjamie			param->value.iov_base = malloc(MAXPATHLEN);
575192896Sjamie			if (param->value.iov_base == NULL)
576192896Sjamie				err(1, "malloc");
577192896Sjamie			if (realpath(value, param->value.iov_base) == NULL)
578192896Sjamie				err(1, "%s: realpath(%s)", name, value);
579192896Sjamie			if (chdir(param->value.iov_base) != 0)
580192896Sjamie				err(1, "chdir: %s",
581192896Sjamie				    (char *)param->value.iov_base);
582192896Sjamie		} else
583192896Sjamie			param->value.iov_base = value;
584192896Sjamie		param->value.iov_len = strlen(param->value.iov_base) + 1;
585192896Sjamie		return;
586192896Sjamie	default:
587192896Sjamie		errx(1, "%s: unknown parameter type %d (%s)",
588192896Sjamie		    name, buf.i, buf.s);
589192896Sjamie	}
590192896Sjamie	param->value.iov_base = malloc(param->value.iov_len);
591192896Sjamie	for (i = 0; i < nval; i++) {
592192896Sjamie		switch (buf.i & CTLTYPE) {
593192896Sjamie		case CTLTYPE_INT:
594192896Sjamie			((int *)param->value.iov_base)[i] =
595192896Sjamie			    strtol(value, &ep, 10);
596192896Sjamie			if (ep[0] != '\0')
597192896Sjamie				errx(1, "%s: non-integer value \"%s\"",
598192896Sjamie				    name, value);
599192896Sjamie			break;
600192896Sjamie		case CTLTYPE_UINT:
601192896Sjamie			((unsigned *)param->value.iov_base)[i] =
602192896Sjamie			    strtoul(value, &ep, 10);
603192896Sjamie			if (ep[0] != '\0')
604192896Sjamie				errx(1, "%s: non-integer value \"%s\"",
605192896Sjamie				    name, value);
606192896Sjamie			break;
607192896Sjamie		case CTLTYPE_LONG:
608192896Sjamie			((long *)param->value.iov_base)[i] =
609192896Sjamie			    strtol(value, &ep, 10);
610192896Sjamie			if (ep[0] != '\0')
611192896Sjamie			    errx(1, "%s: non-integer value \"%s\"",
612192896Sjamie				name, value);
613192896Sjamie			break;
614192896Sjamie		case CTLTYPE_ULONG:
615192896Sjamie			((unsigned long *)param->value.iov_base)[i] =
616192896Sjamie			    strtoul(value, &ep, 10);
617192896Sjamie			if (ep[0] != '\0')
618192896Sjamie			    errx(1, "%s: non-integer value \"%s\"",
619192896Sjamie				name, value);
620192896Sjamie			break;
621192896Sjamie		case CTLTYPE_STRUCT:
622192896Sjamie			if (!strcmp(buf.s, "S,in_addr")) {
623192896Sjamie				if (inet_pton(AF_INET, value,
624192896Sjamie				    &((struct in_addr *)
625192896Sjamie				    param->value.iov_base)[i]) != 1)
626192896Sjamie					errx(1, "%s: not an IPv4 address: %s",
627192896Sjamie					    name, value);
628192896Sjamie			}
629192896Sjamie#ifdef INET6
630192896Sjamie			else if (!strcmp(buf.s, "S,in6_addr")) {
631192896Sjamie				if (inet_pton(AF_INET6, value,
632192896Sjamie				    &((struct in6_addr *)
633192896Sjamie				    param->value.iov_base)[i]) != 1)
634192896Sjamie					errx(1, "%s: not an IPv6 address: %s",
635192896Sjamie					    name, value);
636192896Sjamie			}
637192896Sjamie#endif
638192896Sjamie		}
639192896Sjamie		if (i > 0)
640192896Sjamie			value[-1] = ',';
641192896Sjamie		value = strchr(value, '\0') + 1;
642192896Sjamie	}
643192896Sjamie}
644185435Sbz
645192896Sjamiestatic void
646192896Sjamieusage(void)
647192896Sjamie{
648192896Sjamie
649192896Sjamie	(void)fprintf(stderr,
650192896Sjamie	    "usage: jail [-d] [-h] [-i] [-J jid_file] "
651192896Sjamie			"[-l -u username | -U username]\n"
652192896Sjamie	    "            [-c | -m] param=value ... [command=command ...]\n"
653192896Sjamie	    "       jail [-r jail]\n");
654192896Sjamie	exit(1);
655185435Sbz}
656