jail.c revision 192896
1/*-
2 * Copyright (c) 1999 Poul-Henning Kamp.
3 * Copyright (c) 2009 James Gritton
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/usr.sbin/jail/jail.c 192896 2009-05-27 14:30:26Z jamie $");
30
31#include <sys/param.h>
32#include <sys/jail.h>
33#include <sys/socket.h>
34#include <sys/sysctl.h>
35#include <sys/uio.h>
36
37#include <arpa/inet.h>
38#include <netinet/in.h>
39
40#include <ctype.h>
41#include <err.h>
42#include <errno.h>
43#include <grp.h>
44#include <login_cap.h>
45#include <netdb.h>
46#include <paths.h>
47#include <pwd.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <unistd.h>
52
53#define	SJPARAM		"security.jail.param"
54#define	ERRMSG_SIZE	256
55
56struct param {
57	struct iovec name;
58	struct iovec value;
59};
60
61static struct param *params;
62static char **param_values;
63static int nparams;
64
65static char *ip4_addr;
66#ifdef INET6
67static char *ip6_addr;
68#endif
69
70static void add_ip_addr(char **addrp, char *newaddr);
71#ifdef INET6
72static void add_ip_addr46(char *newaddr);
73#endif
74static void add_ip_addrinfo(int ai_flags, char *value);
75static void quoted_print(FILE *fp, char *str);
76static void set_param(const char *name, char *value);
77static void usage(void);
78
79extern char **environ;
80
81#define GET_USER_INFO do {						\
82	pwd = getpwnam(username);					\
83	if (pwd == NULL) {						\
84		if (errno)						\
85			err(1, "getpwnam: %s", username);		\
86		else							\
87			errx(1, "%s: no such user", username);		\
88	}								\
89	lcap = login_getpwclass(pwd);					\
90	if (lcap == NULL)						\
91		err(1, "getpwclass: %s", username);			\
92	ngroups = NGROUPS;						\
93	if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0)	\
94		err(1, "getgrouplist: %s", username);			\
95} while (0)
96
97int
98main(int argc, char **argv)
99{
100	login_cap_t *lcap = NULL;
101	struct iovec rparams[2];
102	struct passwd *pwd = NULL;
103	gid_t groups[NGROUPS];
104	int ch, cmdarg, i, jail_set_flags, jid, ngroups;
105	int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag;
106	char *ep, *jailname, *securelevel, *username, *JidFile;
107	char errmsg[ERRMSG_SIZE];
108	static char *cleanenv;
109	const char *shell, *p = NULL;
110	FILE *fp;
111
112	hflag = iflag = Jflag = lflag = rflag = uflag = Uflag =
113	    jail_set_flags = 0;
114	cmdarg = jid = -1;
115	jailname = securelevel = username = JidFile = cleanenv = NULL;
116	fp = NULL;
117
118	while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) {
119		switch (ch) {
120		case 'd':
121			jail_set_flags |= JAIL_DYING;
122			break;
123		case 'h':
124			hflag = 1;
125			break;
126		case 'i':
127			iflag = 1;
128			break;
129		case 'J':
130			JidFile = optarg;
131			Jflag = 1;
132			break;
133		case 'n':
134			jailname = optarg;
135			break;
136		case 's':
137			securelevel = optarg;
138			break;
139		case 'u':
140			username = optarg;
141			uflag = 1;
142			break;
143		case 'U':
144			username = optarg;
145			Uflag = 1;
146			break;
147		case 'l':
148			lflag = 1;
149			break;
150		case 'c':
151			jail_set_flags |= JAIL_CREATE;
152			break;
153		case 'm':
154			jail_set_flags |= JAIL_UPDATE;
155			break;
156		case 'r':
157			jid = strtoul(optarg, &ep, 10);
158			if (!*optarg || *ep) {
159				*(const void **)&rparams[0].iov_base = "name";
160				rparams[0].iov_len = sizeof("name");
161				rparams[1].iov_base = optarg;
162				rparams[1].iov_len = strlen(optarg) + 1;
163				jid = jail_get(rparams, 2, 0);
164				if (jid < 0)
165					errx(1, "unknown jail: %s", optarg);
166			}
167			rflag = 1;
168			break;
169		default:
170			usage();
171		}
172	}
173	argc -= optind;
174	argv += optind;
175	if (rflag) {
176		if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag)
177			usage();
178		if (jail_remove(jid) < 0)
179			err(1, "jail_remove");
180		exit (0);
181	}
182	if (argc == 0)
183		usage();
184	if (uflag && Uflag)
185		usage();
186	if (lflag && username == NULL)
187		usage();
188	if (uflag)
189		GET_USER_INFO;
190
191	/*
192	 * If the first argument (path) starts with a slash, and the third
193	 * argument (IP address) starts with a digit, it is likely to be
194	 * an old-style fixed-parameter command line.
195	 */
196	if (jailname)
197		set_param("name", jailname);
198	if (securelevel)
199		set_param("securelevel", securelevel);
200	if (jail_set_flags) {
201		for (i = 0; i < argc; i++) {
202			if (!strncmp(argv[i], "command=", 8)) {
203				cmdarg = i;
204				argv[cmdarg] += 8;
205				jail_set_flags |= JAIL_ATTACH;
206				break;
207			}
208			if (hflag) {
209				if (!strncmp(argv[i], "ip4.addr=", 9)) {
210					add_ip_addr(&ip4_addr, argv[i] + 9);
211					break;
212				}
213#ifdef INET6
214				if (!strncmp(argv[i], "ip6.addr=", 9)) {
215					add_ip_addr(&ip6_addr, argv[i] + 9);
216					break;
217				}
218#endif
219				if (!strncmp(argv[i], "host.hostname=", 14))
220					add_ip_addrinfo(0, argv[i] + 14);
221			}
222			set_param(NULL, argv[i]);
223		}
224	} else {
225		if (argc < 4 || argv[0][0] != '/')
226			errx(1, "%s\n%s",
227			   "no -c or -m, so this must be an old-style command.",
228			   "But it doesn't look like one.");
229		set_param("path", argv[0]);
230		set_param("host.hostname", argv[1]);
231		if (hflag)
232			add_ip_addrinfo(0, argv[1]);
233#ifdef INET6
234		add_ip_addr46(argv[2]);
235#else
236		add_ip_addr(&ip4_addr, argv[2]);
237#endif
238		cmdarg = 3;
239	}
240	if (ip4_addr != NULL)
241		set_param("ip4.addr", ip4_addr);
242#ifdef INET6
243	if (ip6_addr != NULL)
244		set_param("ip6.addr", ip6_addr);
245#endif
246	errmsg[0] = 0;
247	set_param("errmsg", errmsg);
248
249	if (Jflag) {
250		fp = fopen(JidFile, "w");
251		if (fp == NULL)
252			errx(1, "Could not create JidFile: %s", JidFile);
253	}
254	jid = jail_set(&params->name, 2 * nparams,
255	    jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH);
256	if (jid < 0) {
257		if (errmsg[0] != '\0')
258			errx(1, "%s", errmsg);
259		err(1, "jail_set");
260	}
261	if (iflag) {
262		printf("%d\n", jid);
263		fflush(stdout);
264	}
265	if (Jflag) {
266		if (jail_set_flags) {
267			fprintf(fp, "jid=%d", jid);
268			for (i = 0; i < nparams; i++)
269				if (strcmp(params[i].name.iov_base, "jid") &&
270				    strcmp(params[i].name.iov_base, "errmsg")) {
271					fprintf(fp, " %s",
272					    (char *)params[i].name.iov_base);
273					if (param_values[i]) {
274						putc('=', fp);
275						quoted_print(fp,
276						    param_values[i]);
277					}
278				}
279			fprintf(fp, "\n");
280		} else {
281			for (i = 0; i < nparams; i++)
282				if (!strcmp(params[i].name.iov_base, "path"))
283					break;
284#ifdef INET6
285			fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n",
286			    jid, i < nparams
287			    ? (char *)params[i].value.iov_base : argv[0],
288			    argv[1], ip4_addr ? ip4_addr : "",
289			    ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0]
290			    ? "," : "", ip6_addr ? ip6_addr : "", argv[3]);
291#else
292			fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
293			    jid, i < nparams
294			    ? (char *)params[i].value.iov_base : argv[0],
295			    argv[1], ip4_addr ? ip4_addr : "", argv[3]);
296#endif
297		}
298		(void)fclose(fp);
299	}
300	if (cmdarg < 0)
301		exit(0);
302	if (username != NULL) {
303		if (Uflag)
304			GET_USER_INFO;
305		if (lflag) {
306			p = getenv("TERM");
307			environ = &cleanenv;
308		}
309		if (setgroups(ngroups, groups) != 0)
310			err(1, "setgroups");
311		if (setgid(pwd->pw_gid) != 0)
312			err(1, "setgid");
313		if (setusercontext(lcap, pwd, pwd->pw_uid,
314		    LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
315			err(1, "setusercontext");
316		login_close(lcap);
317	}
318	if (lflag) {
319		if (*pwd->pw_shell)
320			shell = pwd->pw_shell;
321		else
322			shell = _PATH_BSHELL;
323		if (chdir(pwd->pw_dir) < 0)
324			errx(1, "no home directory");
325		setenv("HOME", pwd->pw_dir, 1);
326		setenv("SHELL", shell, 1);
327		setenv("USER", pwd->pw_name, 1);
328		if (p)
329			setenv("TERM", p, 1);
330	}
331	execvp(argv[cmdarg], argv + cmdarg);
332	err(1, "execvp: %s", argv[cmdarg]);
333}
334
335static void
336add_ip_addr(char **addrp, char *value)
337{
338	int addrlen;
339	char *addr;
340
341	if (!*addrp) {
342		*addrp = strdup(value);
343		if (!*addrp)
344			err(1, "malloc");
345	} else if (value[0]) {
346		addrlen = strlen(*addrp) + strlen(value) + 2;
347		addr = malloc(addrlen);
348		if (!addr)
349			err(1, "malloc");
350		snprintf(addr, addrlen, "%s,%s", *addrp, value);
351		free(*addrp);
352		*addrp = addr;
353	}
354}
355
356#ifdef INET6
357static void
358add_ip_addr46(char *value)
359{
360	char *p, *np;
361
362	if (!value[0]) {
363		add_ip_addr(&ip4_addr, value);
364		add_ip_addr(&ip6_addr, value);
365		return;
366	}
367	for (p = value;; p = np + 1)
368	{
369		np = strchr(p, ',');
370		if (np)
371			*np = '\0';
372		add_ip_addrinfo(AI_NUMERICHOST, p);
373		if (!np)
374			break;
375	}
376}
377#endif
378
379static void
380add_ip_addrinfo(int ai_flags, char *value)
381{
382	struct addrinfo hints, *ai0, *ai;
383	struct in_addr addr4;
384	int error;
385	char avalue4[INET_ADDRSTRLEN];
386#ifdef INET6
387	struct in6_addr addr6;
388	char avalue6[INET6_ADDRSTRLEN];
389#endif
390
391	/* Look up the hostname (or get the address) */
392	memset(&hints, 0, sizeof(hints));
393	hints.ai_socktype = SOCK_STREAM;
394#ifdef INET6
395	hints.ai_family = PF_UNSPEC;
396#else
397	hints.ai_family = PF_INET;
398#endif
399	hints.ai_flags = ai_flags;
400	error = getaddrinfo(value, NULL, &hints, &ai0);
401	if (error != 0)
402		errx(1, "hostname %s: %s", value, gai_strerror(error));
403
404	/* Convert the addresses to ASCII so set_param can convert them back. */
405	for (ai = ai0; ai; ai = ai->ai_next)
406		switch (ai->ai_family) {
407		case AF_INET:
408			memcpy(&addr4, &((struct sockaddr_in *)
409			    (void *)ai->ai_addr)->sin_addr, sizeof(addr4));
410			if (inet_ntop(AF_INET, &addr4, avalue4,
411			    INET_ADDRSTRLEN) == NULL)
412				err(1, "inet_ntop");
413			add_ip_addr(&ip4_addr, avalue4);
414			break;
415#ifdef INET6
416		case AF_INET6:
417			memcpy(&addr6, &((struct sockaddr_in6 *)
418			    (void *)ai->ai_addr)->sin6_addr, sizeof(addr6));
419			if (inet_ntop(AF_INET6, &addr6, avalue6,
420			    INET6_ADDRSTRLEN) == NULL)
421				err(1, "inet_ntop");
422			add_ip_addr(&ip6_addr, avalue6);
423			break;
424#endif
425		}
426	freeaddrinfo(ai0);
427}
428
429static void
430quoted_print(FILE *fp, char *str)
431{
432	int c, qc;
433	char *p = str;
434
435	/* An empty string needs quoting. */
436	if (!*p) {
437		fputs("\"\"", fp);
438		return;
439	}
440
441	/*
442	 * The value will be surrounded by quotes if it contains spaces
443	 * or quotes.
444	 */
445	qc = strchr(p, '\'') ? '"'
446	    : strchr(p, '"') ? '\''
447	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
448	    : 0;
449	if (qc)
450		putc(qc, fp);
451	while ((c = *p++)) {
452		if (c == '\\' || c == qc)
453			putc('\\', fp);
454		putc(c, fp);
455	}
456	if (qc)
457		putc(qc, fp);
458}
459
460static void
461set_param(const char *name, char *value)
462{
463	struct param *param;
464	char *ep, *p;
465	size_t buflen, mlen;
466	int i, nval, mib[CTL_MAXNAME];
467	struct {
468		int i;
469		char s[MAXPATHLEN];
470	} buf;
471
472	static int paramlistsize;
473
474	/* Separate the name from the value, if not done already. */
475	if (name == NULL) {
476		name = value;
477		if ((value = strchr(value, '=')))
478			*value++ = '\0';
479	}
480
481	/* Check for repeat parameters */
482	for (i = 0; i < nparams; i++)
483		if (!strcmp(name, params[i].name.iov_base)) {
484			memcpy(params + i, params + i + 1,
485			    (--nparams - i) * sizeof(struct param));
486			break;
487		}
488
489	/* Make sure there is room for the new param record. */
490	if (!nparams) {
491		paramlistsize = 32;
492		params = malloc(paramlistsize * sizeof(*params));
493		param_values = malloc(paramlistsize * sizeof(*param_values));
494		if (params == NULL || param_values == NULL)
495			err(1, "malloc");
496	} else if (nparams >= paramlistsize) {
497		paramlistsize *= 2;
498		params = realloc(params, paramlistsize * sizeof(*params));
499		param_values = realloc(param_values,
500		    paramlistsize * sizeof(*param_values));
501		if (params == NULL)
502			err(1, "realloc");
503	}
504
505	/* Look up the paramter. */
506	param_values[nparams] = value;
507	param = params + nparams++;
508	*(const void **)&param->name.iov_base = name;
509	param->name.iov_len = strlen(name) + 1;
510	/* Trivial values - no value or errmsg. */
511	if (value == NULL) {
512		param->value.iov_base = NULL;
513		param->value.iov_len = 0;
514		return;
515	}
516	if (!strcmp(name, "errmsg")) {
517		param->value.iov_base = value;
518		param->value.iov_len = ERRMSG_SIZE;
519		return;
520	}
521	mib[0] = 0;
522	mib[1] = 3;
523	snprintf(buf.s, sizeof(buf.s), SJPARAM ".%s", name);
524	mlen = sizeof(mib) - 2 * sizeof(int);
525	if (sysctl(mib, 2, mib + 2, &mlen, buf.s, strlen(buf.s)) < 0)
526		errx(1, "unknown parameter: %s", name);
527	mib[1] = 4;
528	buflen = sizeof(buf);
529	if (sysctl(mib, (mlen / sizeof(int)) + 2, &buf, &buflen, NULL, 0) < 0)
530		err(1, "sysctl(0.4.%s)", name);
531	/*
532	 * See if this is an array type.
533	 * Treat non-arrays as an array of one.
534	 */
535	p = strchr(buf.s, '\0');
536	nval = 1;
537	if (p - 2 >= buf.s && !strcmp(p - 2, ",a")) {
538		if (value[0] == '\0' ||
539		    (value[0] == '-' && value[1] == '\0')) {
540			param->value.iov_base = value;
541			param->value.iov_len = 0;
542			return;
543		}
544		p[-2] = 0;
545		for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) {
546			*p = '\0';
547			nval++;
548		}
549	}
550
551	/* Set the values according to the parameter type. */
552	switch (buf.i & CTLTYPE) {
553	case CTLTYPE_INT:
554	case CTLTYPE_UINT:
555		param->value.iov_len = nval * sizeof(int);
556		break;
557	case CTLTYPE_LONG:
558	case CTLTYPE_ULONG:
559		param->value.iov_len = nval * sizeof(long);
560		break;
561	case CTLTYPE_STRUCT:
562		if (!strcmp(buf.s, "S,in_addr"))
563			param->value.iov_len = nval * sizeof(struct in_addr);
564#ifdef INET6
565		else if (!strcmp(buf.s, "S,in6_addr"))
566			param->value.iov_len = nval * sizeof(struct in6_addr);
567#endif
568		else
569			errx(1, "%s: unknown parameter structure (%s)",
570			    name, buf.s);
571		break;
572	case CTLTYPE_STRING:
573		if (!strcmp(name, "path")) {
574			param->value.iov_base = malloc(MAXPATHLEN);
575			if (param->value.iov_base == NULL)
576				err(1, "malloc");
577			if (realpath(value, param->value.iov_base) == NULL)
578				err(1, "%s: realpath(%s)", name, value);
579			if (chdir(param->value.iov_base) != 0)
580				err(1, "chdir: %s",
581				    (char *)param->value.iov_base);
582		} else
583			param->value.iov_base = value;
584		param->value.iov_len = strlen(param->value.iov_base) + 1;
585		return;
586	default:
587		errx(1, "%s: unknown parameter type %d (%s)",
588		    name, buf.i, buf.s);
589	}
590	param->value.iov_base = malloc(param->value.iov_len);
591	for (i = 0; i < nval; i++) {
592		switch (buf.i & CTLTYPE) {
593		case CTLTYPE_INT:
594			((int *)param->value.iov_base)[i] =
595			    strtol(value, &ep, 10);
596			if (ep[0] != '\0')
597				errx(1, "%s: non-integer value \"%s\"",
598				    name, value);
599			break;
600		case CTLTYPE_UINT:
601			((unsigned *)param->value.iov_base)[i] =
602			    strtoul(value, &ep, 10);
603			if (ep[0] != '\0')
604				errx(1, "%s: non-integer value \"%s\"",
605				    name, value);
606			break;
607		case CTLTYPE_LONG:
608			((long *)param->value.iov_base)[i] =
609			    strtol(value, &ep, 10);
610			if (ep[0] != '\0')
611			    errx(1, "%s: non-integer value \"%s\"",
612				name, value);
613			break;
614		case CTLTYPE_ULONG:
615			((unsigned long *)param->value.iov_base)[i] =
616			    strtoul(value, &ep, 10);
617			if (ep[0] != '\0')
618			    errx(1, "%s: non-integer value \"%s\"",
619				name, value);
620			break;
621		case CTLTYPE_STRUCT:
622			if (!strcmp(buf.s, "S,in_addr")) {
623				if (inet_pton(AF_INET, value,
624				    &((struct in_addr *)
625				    param->value.iov_base)[i]) != 1)
626					errx(1, "%s: not an IPv4 address: %s",
627					    name, value);
628			}
629#ifdef INET6
630			else if (!strcmp(buf.s, "S,in6_addr")) {
631				if (inet_pton(AF_INET6, value,
632				    &((struct in6_addr *)
633				    param->value.iov_base)[i]) != 1)
634					errx(1, "%s: not an IPv6 address: %s",
635					    name, value);
636			}
637#endif
638		}
639		if (i > 0)
640			value[-1] = ',';
641		value = strchr(value, '\0') + 1;
642	}
643}
644
645static void
646usage(void)
647{
648
649	(void)fprintf(stderr,
650	    "usage: jail [-d] [-h] [-i] [-J jid_file] "
651			"[-l -u username | -U username]\n"
652	    "            [-c | -m] param=value ... [command=command ...]\n"
653	    "       jail [-r jail]\n");
654	exit(1);
655}
656