xref: /freebsd/usr.sbin/jail/jail.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
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$");
30 
31 #include <sys/param.h>
32 #include <sys/jail.h>
33 #include <sys/socket.h>
34 #include <sys/sysctl.h>
35 
36 #include <arpa/inet.h>
37 #include <netinet/in.h>
38 
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <grp.h>
43 #include <jail.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 static struct jailparam *params;
54 static char **param_values;
55 static int nparams;
56 
57 #ifdef INET6
58 static int ip6_ok;
59 static char *ip6_addr;
60 #endif
61 #ifdef INET
62 static int ip4_ok;
63 static char *ip4_addr;
64 #endif
65 
66 #if defined(INET6) || defined(INET)
67 static void add_ip_addr(char **addrp, char *newaddr);
68 #endif
69 #ifdef INET6
70 static void add_ip_addr46(char *newaddr);
71 #endif
72 static void add_ip_addrinfo(int ai_flags, char *value);
73 static void quoted_print(FILE *fp, char *str);
74 static void set_param(const char *name, char *value);
75 static void usage(void);
76 
77 static const char *perm_sysctl[][3] = {
78 	{ "security.jail.set_hostname_allowed",
79 	  "allow.noset_hostname", "allow.set_hostname" },
80 	{ "security.jail.sysvipc_allowed",
81 	  "allow.nosysvipc", "allow.sysvipc" },
82 	{ "security.jail.allow_raw_sockets",
83 	  "allow.noraw_sockets", "allow.raw_sockets" },
84 	{ "security.jail.chflags_allowed",
85 	  "allow.nochflags", "allow.chflags" },
86 	{ "security.jail.mount_allowed",
87 	  "allow.nomount", "allow.mount" },
88 	{ "security.jail.socket_unixiproute_only",
89 	  "allow.socket_af", "allow.nosocket_af" },
90 };
91 
92 extern char **environ;
93 
94 #define GET_USER_INFO do {						\
95 	pwd = getpwnam(username);					\
96 	if (pwd == NULL) {						\
97 		if (errno)						\
98 			err(1, "getpwnam: %s", username);		\
99 		else							\
100 			errx(1, "%s: no such user", username);		\
101 	}								\
102 	lcap = login_getpwclass(pwd);					\
103 	if (lcap == NULL)						\
104 		err(1, "getpwclass: %s", username);			\
105 	ngroups = ngroups_max;						\
106 	if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0)	\
107 		err(1, "getgrouplist: %s", username);			\
108 } while (0)
109 
110 int
111 main(int argc, char **argv)
112 {
113 	login_cap_t *lcap = NULL;
114 	struct passwd *pwd = NULL;
115 	gid_t *groups;
116 	size_t sysvallen;
117 	int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval;
118 	int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag;
119 	long ngroups_max;
120 	unsigned pi;
121 	char *jailname, *securelevel, *username, *JidFile;
122 	char enforce_statfs[4];
123 	static char *cleanenv;
124 	const char *shell, *p = NULL;
125 	FILE *fp;
126 
127 	hflag = iflag = Jflag = lflag = rflag = uflag = Uflag =
128 	    jail_set_flags = 0;
129 	cmdarg = jid = -1;
130 	jailname = securelevel = username = JidFile = cleanenv = NULL;
131 	fp = NULL;
132 
133 	ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
134 	if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
135 		err(1, "malloc");
136 
137 	while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) {
138 		switch (ch) {
139 		case 'd':
140 			jail_set_flags |= JAIL_DYING;
141 			break;
142 		case 'h':
143 			hflag = 1;
144 			break;
145 		case 'i':
146 			iflag = 1;
147 			break;
148 		case 'J':
149 			JidFile = optarg;
150 			Jflag = 1;
151 			break;
152 		case 'n':
153 			jailname = optarg;
154 			break;
155 		case 's':
156 			securelevel = optarg;
157 			break;
158 		case 'u':
159 			username = optarg;
160 			uflag = 1;
161 			break;
162 		case 'U':
163 			username = optarg;
164 			Uflag = 1;
165 			break;
166 		case 'l':
167 			lflag = 1;
168 			break;
169 		case 'c':
170 			jail_set_flags |= JAIL_CREATE;
171 			break;
172 		case 'm':
173 			jail_set_flags |= JAIL_UPDATE;
174 			break;
175 		case 'r':
176 			jid = jail_getid(optarg);
177 			if (jid < 0)
178 				errx(1, "%s", jail_errmsg);
179 			rflag = 1;
180 			break;
181 		default:
182 			usage();
183 		}
184 	}
185 	argc -= optind;
186 	argv += optind;
187 	if (rflag) {
188 		if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag)
189 			usage();
190 		if (jail_remove(jid) < 0)
191 			err(1, "jail_remove");
192 		exit (0);
193 	}
194 	if (argc == 0)
195 		usage();
196 	if (uflag && Uflag)
197 		usage();
198 	if (lflag && username == NULL)
199 		usage();
200 	if (uflag)
201 		GET_USER_INFO;
202 
203 #ifdef INET6
204 	ip6_ok = feature_present("inet6");
205 #endif
206 #ifdef INET
207 	ip4_ok = feature_present("inet");
208 #endif
209 
210 	if (jailname)
211 		set_param("name", jailname);
212 	if (securelevel)
213 		set_param("securelevel", securelevel);
214 	if (jail_set_flags) {
215 		for (i = 0; i < argc; i++) {
216 			if (!strncmp(argv[i], "command=", 8)) {
217 				cmdarg = i;
218 				argv[cmdarg] += 8;
219 				jail_set_flags |= JAIL_ATTACH;
220 				break;
221 			}
222 			if (hflag) {
223 #ifdef INET
224 				if (!strncmp(argv[i], "ip4.addr=", 9)) {
225 					add_ip_addr(&ip4_addr, argv[i] + 9);
226 					break;
227 				}
228 #endif
229 #ifdef INET6
230 				if (!strncmp(argv[i], "ip6.addr=", 9)) {
231 					add_ip_addr(&ip6_addr, argv[i] + 9);
232 					break;
233 				}
234 #endif
235 				if (!strncmp(argv[i], "host.hostname=", 14))
236 					add_ip_addrinfo(0, argv[i] + 14);
237 			}
238 			set_param(NULL, argv[i]);
239 		}
240 	} else {
241 		if (argc < 4 || argv[0][0] != '/')
242 			errx(1, "%s\n%s",
243 			   "no -c or -m, so this must be an old-style command.",
244 			   "But it doesn't look like one.");
245 		set_param("path", argv[0]);
246 		set_param("host.hostname", argv[1]);
247 		if (hflag)
248 			add_ip_addrinfo(0, argv[1]);
249 #if defined(INET6) || defined(INET)
250 		if (argv[2][0] != '\0')
251 #ifdef INET6
252 			add_ip_addr46(argv[2]);
253 #else
254 			add_ip_addr(&ip4_addr, argv[2]);
255 #endif
256 #endif
257 		cmdarg = 3;
258 		/* Emulate the defaults from security.jail.* sysctls */
259 		sysvallen = sizeof(sysval);
260 		if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
261 		    NULL, 0) == 0 && sysval == 0) {
262 			for (pi = 0; pi < sizeof(perm_sysctl) /
263 			     sizeof(perm_sysctl[0]); pi++) {
264 				sysvallen = sizeof(sysval);
265 				if (sysctlbyname(perm_sysctl[pi][0],
266 				    &sysval, &sysvallen, NULL, 0) == 0)
267 					set_param(perm_sysctl[pi]
268 					    [sysval ? 2 : 1], NULL);
269 			}
270 			sysvallen = sizeof(sysval);
271 			if (sysctlbyname("security.jail.enforce_statfs",
272 			    &sysval, &sysvallen, NULL, 0) == 0) {
273 				snprintf(enforce_statfs,
274 				    sizeof(enforce_statfs), "%d", sysval);
275 				set_param("enforce_statfs", enforce_statfs);
276 			}
277 		}
278 	}
279 #ifdef INET
280 	if (ip4_addr != NULL)
281 		set_param("ip4.addr", ip4_addr);
282 #endif
283 #ifdef INET6
284 	if (ip6_addr != NULL)
285 		set_param("ip6.addr", ip6_addr);
286 #endif
287 
288 	if (Jflag) {
289 		fp = fopen(JidFile, "w");
290 		if (fp == NULL)
291 			errx(1, "Could not create JidFile: %s", JidFile);
292 	}
293 	jid = jailparam_set(params, nparams,
294 	    jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH);
295 	if (jid < 0)
296 		errx(1, "%s", jail_errmsg);
297 	if (iflag) {
298 		printf("%d\n", jid);
299 		fflush(stdout);
300 	}
301 	if (Jflag) {
302 		if (jail_set_flags) {
303 			fprintf(fp, "jid=%d", jid);
304 			for (i = 0; i < nparams; i++)
305 				if (strcmp(params[i].jp_name, "jid")) {
306 					fprintf(fp, " %s",
307 					    (char *)params[i].jp_name);
308 					if (param_values[i]) {
309 						putc('=', fp);
310 						quoted_print(fp,
311 						    param_values[i]);
312 					}
313 				}
314 			fprintf(fp, "\n");
315 		} else {
316 			for (i = 0; i < nparams; i++)
317 				if (!strcmp(params[i].jp_name, "path"))
318 					break;
319 #if defined(INET6) && defined(INET)
320 			fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n",
321 			    jid, i < nparams
322 			    ? (char *)params[i].jp_value : argv[0],
323 			    argv[1], ip4_addr ? ip4_addr : "",
324 			    ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0]
325 			    ? "," : "", ip6_addr ? ip6_addr : "", argv[3]);
326 #elif defined(INET6)
327 			fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
328 			    jid, i < nparams
329 			    ?  (char *)params[i].jp_value : argv[0],
330 			    argv[1], ip6_addr ? ip6_addr : "", argv[3]);
331 #elif defined(INET)
332 			fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
333 			    jid, i < nparams
334 			    ? (char *)params[i].jp_value : argv[0],
335 			    argv[1], ip4_addr ? ip4_addr : "", argv[3]);
336 #endif
337 		}
338 		(void)fclose(fp);
339 	}
340 	if (cmdarg < 0)
341 		exit(0);
342 	if (username != NULL) {
343 		if (Uflag)
344 			GET_USER_INFO;
345 		if (lflag) {
346 			p = getenv("TERM");
347 			environ = &cleanenv;
348 		}
349 		if (setgroups(ngroups, groups) != 0)
350 			err(1, "setgroups");
351 		if (setgid(pwd->pw_gid) != 0)
352 			err(1, "setgid");
353 		if (setusercontext(lcap, pwd, pwd->pw_uid,
354 		    LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
355 			err(1, "setusercontext");
356 		login_close(lcap);
357 	}
358 	if (lflag) {
359 		if (*pwd->pw_shell)
360 			shell = pwd->pw_shell;
361 		else
362 			shell = _PATH_BSHELL;
363 		if (chdir(pwd->pw_dir) < 0)
364 			errx(1, "no home directory");
365 		setenv("HOME", pwd->pw_dir, 1);
366 		setenv("SHELL", shell, 1);
367 		setenv("USER", pwd->pw_name, 1);
368 		if (p)
369 			setenv("TERM", p, 1);
370 	}
371 	execvp(argv[cmdarg], argv + cmdarg);
372 	err(1, "execvp: %s", argv[cmdarg]);
373 }
374 
375 #if defined(INET6) || defined(INET)
376 static void
377 add_ip_addr(char **addrp, char *value)
378 {
379 	int addrlen;
380 	char *addr;
381 
382 	if (!*addrp) {
383 		*addrp = strdup(value);
384 		if (!*addrp)
385 			err(1, "malloc");
386 	} else if (value[0]) {
387 		addrlen = strlen(*addrp) + strlen(value) + 2;
388 		addr = malloc(addrlen);
389 		if (!addr)
390 			err(1, "malloc");
391 		snprintf(addr, addrlen, "%s,%s", *addrp, value);
392 		free(*addrp);
393 		*addrp = addr;
394 	}
395 }
396 #endif
397 
398 #ifdef INET6
399 static void
400 add_ip_addr46(char *value)
401 {
402 	char *p, *np;
403 
404 	for (p = value;; p = np + 1)
405 	{
406 		np = strchr(p, ',');
407 		if (np)
408 			*np = '\0';
409 		add_ip_addrinfo(AI_NUMERICHOST, p);
410 		if (!np)
411 			break;
412 	}
413 }
414 #endif
415 
416 static void
417 add_ip_addrinfo(int ai_flags, char *value)
418 {
419 	struct addrinfo hints, *ai0, *ai;
420 	int error;
421 #ifdef INET
422 	char avalue4[INET_ADDRSTRLEN];
423 	struct in_addr addr4;
424 #endif
425 #ifdef INET6
426 	char avalue6[INET6_ADDRSTRLEN];
427 	struct in6_addr addr6;
428 #endif
429 
430 	/* Look up the hostname (or get the address) */
431 	memset(&hints, 0, sizeof(hints));
432 	hints.ai_socktype = SOCK_STREAM;
433 #if defined(INET6) && defined(INET)
434 	hints.ai_family = PF_UNSPEC;
435 #elif defined(INET6)
436 	hints.ai_family = PF_INET6;
437 #elif defined(INET)
438 	hints.ai_family = PF_INET;
439 #endif
440 	hints.ai_flags = ai_flags;
441 	error = getaddrinfo(value, NULL, &hints, &ai0);
442 	if (error != 0)
443 		errx(1, "hostname %s: %s", value, gai_strerror(error));
444 
445 	/* Convert the addresses to ASCII so set_param can convert them back. */
446 	for (ai = ai0; ai; ai = ai->ai_next)
447 		switch (ai->ai_family) {
448 #ifdef INET
449 		case AF_INET:
450 			if (!ip4_ok && (ai_flags & AI_NUMERICHOST) == 0)
451 				break;
452 			memcpy(&addr4, &((struct sockaddr_in *)
453 			    (void *)ai->ai_addr)->sin_addr, sizeof(addr4));
454 			if (inet_ntop(AF_INET, &addr4, avalue4,
455 			    INET_ADDRSTRLEN) == NULL)
456 				err(1, "inet_ntop");
457 			add_ip_addr(&ip4_addr, avalue4);
458 			break;
459 #endif
460 #ifdef INET6
461 		case AF_INET6:
462 			if (!ip6_ok && (ai_flags & AI_NUMERICHOST) == 0)
463 				break;
464 			memcpy(&addr6, &((struct sockaddr_in6 *)
465 			    (void *)ai->ai_addr)->sin6_addr, sizeof(addr6));
466 			if (inet_ntop(AF_INET6, &addr6, avalue6,
467 			    INET6_ADDRSTRLEN) == NULL)
468 				err(1, "inet_ntop");
469 			add_ip_addr(&ip6_addr, avalue6);
470 			break;
471 #endif
472 		}
473 	freeaddrinfo(ai0);
474 }
475 
476 static void
477 quoted_print(FILE *fp, char *str)
478 {
479 	int c, qc;
480 	char *p = str;
481 
482 	/* An empty string needs quoting. */
483 	if (!*p) {
484 		fputs("\"\"", fp);
485 		return;
486 	}
487 
488 	/*
489 	 * The value will be surrounded by quotes if it contains spaces
490 	 * or quotes.
491 	 */
492 	qc = strchr(p, '\'') ? '"'
493 	    : strchr(p, '"') ? '\''
494 	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
495 	    : 0;
496 	if (qc)
497 		putc(qc, fp);
498 	while ((c = *p++)) {
499 		if (c == '\\' || c == qc)
500 			putc('\\', fp);
501 		putc(c, fp);
502 	}
503 	if (qc)
504 		putc(qc, fp);
505 }
506 
507 static void
508 set_param(const char *name, char *value)
509 {
510 	struct jailparam *param;
511 	int i;
512 
513 	static int paramlistsize;
514 
515 	/* Separate the name from the value, if not done already. */
516 	if (name == NULL) {
517 		name = value;
518 		if ((value = strchr(value, '=')))
519 			*value++ = '\0';
520 	}
521 
522 	/* jail_set won't chdir along with its chroot, so do it here. */
523 	if (!strcmp(name, "path") && chdir(value) < 0)
524 		err(1, "chdir: %s", value);
525 
526 	/* Check for repeat parameters */
527 	for (i = 0; i < nparams; i++)
528 		if (!strcmp(name, params[i].jp_name)) {
529 			jailparam_free(params + i, 1);
530 			memcpy(params + i, params + i + 1,
531 			    (--nparams - i) * sizeof(struct jailparam));
532 			break;
533 		}
534 
535 	/* Make sure there is room for the new param record. */
536 	if (!nparams) {
537 		paramlistsize = 32;
538 		params = malloc(paramlistsize * sizeof(*params));
539 		param_values = malloc(paramlistsize * sizeof(*param_values));
540 		if (params == NULL || param_values == NULL)
541 			err(1, "malloc");
542 	} else if (nparams >= paramlistsize) {
543 		paramlistsize *= 2;
544 		params = realloc(params, paramlistsize * sizeof(*params));
545 		param_values = realloc(param_values,
546 		    paramlistsize * sizeof(*param_values));
547 		if (params == NULL)
548 			err(1, "realloc");
549 	}
550 
551 	/* Look up the paramter. */
552 	param_values[nparams] = value;
553 	param = params + nparams++;
554 	if (jailparam_init(param, name) < 0 ||
555 	    jailparam_import(param, value) < 0)
556 		errx(1, "%s", jail_errmsg);
557 }
558 
559 static void
560 usage(void)
561 {
562 
563 	(void)fprintf(stderr,
564 	    "usage: jail [-d] [-h] [-i] [-J jid_file] "
565 			"[-l -u username | -U username]\n"
566 	    "            [-c | -m] param=value ... [command=command ...]\n"
567 	    "       jail [-r jail]\n");
568 	exit(1);
569 }
570