1e730a09jamie/*-
27551d83pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
37551d83pfg *
4e730a09jamie * Copyright (c) 1999 Poul-Henning Kamp.
5c3ff4f9jamie * Copyright (c) 2009-2012 James Gritton
6e730a09jamie * All rights reserved.
7e730a09jamie *
8e730a09jamie * Redistribution and use in source and binary forms, with or without
9e730a09jamie * modification, are permitted provided that the following conditions
10e730a09jamie * are met:
11e730a09jamie * 1. Redistributions of source code must retain the above copyright
12e730a09jamie *    notice, this list of conditions and the following disclaimer.
13e730a09jamie * 2. Redistributions in binary form must reproduce the above copyright
14e730a09jamie *    notice, this list of conditions and the following disclaimer in the
15e730a09jamie *    documentation and/or other materials provided with the distribution.
16e730a09jamie *
17e730a09jamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18e730a09jamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19e730a09jamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20e730a09jamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21e730a09jamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e730a09jamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23e730a09jamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24e730a09jamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25e730a09jamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26e730a09jamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27e730a09jamie * SUCH DAMAGE.
28592151fphk */
29592151fphk
3001f9734charnier#include <sys/cdefs.h>
3101f9734charnier__FBSDID("$FreeBSD$");
3201f9734charnier
333b31921jamie#include <sys/types.h>
343b31921jamie#include <sys/stat.h>
35d2730d5bz#include <sys/socket.h>
36e329909matteo#include <sys/sysctl.h>
372328ceadd
382328ceadd#include <arpa/inet.h>
398dbff96jamie#include <netinet/in.h>
402328ceadd
412328ceadd#include <err.h>
42872614cmaxim#include <errno.h>
433b31921jamie#include <stdarg.h>
442328ceadd#include <stdio.h>
452328ceadd#include <stdlib.h>
462328ceadd#include <string.h>
472328ceadd#include <unistd.h>
48ca21a25phk
493b31921jamie#include "jailp.h"
508dbff96jamie
513b31921jamie#define JP_RDTUN(jp)	(((jp)->jp_ctltype & CTLFLAG_RDTUN) == CTLFLAG_RDTUN)
52d2730d5bz
53235aefejamiestruct permspec {
54235aefejamie	const char	*name;
55235aefejamie	enum intparam	ipnum;
56235aefejamie	int		rev;
57235aefejamie};
58235aefejamie
593b31921jamieconst char *cfname;
605ddbe53jamieint iflag;
61b3870a1jamieint note_remove;
623b31921jamieint verbose;
637e81b08eugenconst char *separator = "\t";
643b31921jamie
653b31921jamiestatic void clear_persist(struct cfjail *j);
663b31921jamiestatic int update_jail(struct cfjail *j);
673b31921jamiestatic int rdtun_params(struct cfjail *j, int dofail);
683b31921jamiestatic void running_jid(struct cfjail *j, int dflag);
69a85d762jamiestatic void jail_quoted_warnx(const struct cfjail *j, const char *name_msg,
70a85d762jamie    const char *noname_msg);
713b31921jamiestatic int jailparam_set_note(const struct cfjail *j, struct jailparam *jp,
723b31921jamie    unsigned njp, int flags);
737e81b08eugenstatic void print_jail(FILE *fp, struct cfjail *j, int oldcl, int running);
743b31921jamiestatic void print_param(FILE *fp, const struct cfparam *p, int sep, int doname);
757e81b08eugenstatic void show_jails(void);
768dbff96jamiestatic void quoted_print(FILE *fp, char *str);
778dbff96jamiestatic void usage(void);
788dbff96jamie
79235aefejamiestatic struct permspec perm_sysctl[] = {
808d425bfjamie    { "security.jail.set_hostname_allowed", KP_ALLOW_SET_HOSTNAME, 0 },
818d425bfjamie    { "security.jail.sysvipc_allowed", KP_ALLOW_SYSVIPC, 0 },
828d425bfjamie    { "security.jail.allow_raw_sockets", KP_ALLOW_RAW_SOCKETS, 0 },
838d425bfjamie    { "security.jail.chflags_allowed", KP_ALLOW_CHFLAGS, 0 },
848d425bfjamie    { "security.jail.mount_allowed", KP_ALLOW_MOUNT, 0 },
858d425bfjamie    { "security.jail.socket_unixiproute_only", KP_ALLOW_SOCKET_AF, 1 },
8642b3c32jamie};
8742b3c32jamie
88bf5da84jamiestatic const enum intparam startcommands[] = {
8918b00cejamie    IP__NULL,
909fcd25bfreqlabs    IP_EXEC_PREPARE,
916811668jamie#ifdef INET
92bf5da84jamie    IP__IP4_IFADDR,
936811668jamie#endif
94bf5da84jamie#ifdef INET6
95bf5da84jamie    IP__IP6_IFADDR,
96bf5da84jamie#endif
97bf5da84jamie    IP_MOUNT,
98bf5da84jamie    IP__MOUNT_FROM_FSTAB,
99bf5da84jamie    IP_MOUNT_DEVFS,
100513bdd9hrs    IP_MOUNT_FDESCFS,
1016064614jamie    IP_MOUNT_PROCFS,
1028d19ad1emaste    IP_EXEC_PRESTART,
103bf5da84jamie    IP__OP,
10442ccecbnetchild    IP_EXEC_CREATED,
105bf5da84jamie    IP_VNET_INTERFACE,
106bf5da84jamie    IP_EXEC_START,
107bf5da84jamie    IP_COMMAND,
108bf5da84jamie    IP_EXEC_POSTSTART,
10918b00cejamie    IP__NULL
11042b3c32jamie};
11142b3c32jamie
112bf5da84jamiestatic const enum intparam stopcommands[] = {
11318b00cejamie    IP__NULL,
114bf5da84jamie    IP_EXEC_PRESTOP,
115bf5da84jamie    IP_EXEC_STOP,
116bf5da84jamie    IP_STOP_TIMEOUT,
117bf5da84jamie    IP__OP,
118bf5da84jamie    IP_EXEC_POSTSTOP,
1196064614jamie    IP_MOUNT_PROCFS,
120513bdd9hrs    IP_MOUNT_FDESCFS,
121bf5da84jamie    IP_MOUNT_DEVFS,
122bf5da84jamie    IP__MOUNT_FROM_FSTAB,
123bf5da84jamie    IP_MOUNT,
124bf5da84jamie#ifdef INET6
125bf5da84jamie    IP__IP6_IFADDR,
126bf5da84jamie#endif
1276811668jamie#ifdef INET
128bf5da84jamie    IP__IP4_IFADDR,
1296811668jamie#endif
1309fcd25bfreqlabs    IP_EXEC_RELEASE,
13118b00cejamie    IP__NULL
132bf5da84jamie};
133872614cmaxim
134ca21a25phkint
135ca21a25phkmain(int argc, char **argv)
136ca21a25phk{
1373b31921jamie	struct stat st;
1383b31921jamie	FILE *jfp;
1393b31921jamie	struct cfjail *j;
1406811668jamie	char *JidFile;
14142b3c32jamie	size_t sysvallen;
1423b31921jamie	unsigned op, pi;
1433b31921jamie	int ch, docf, error, i, oldcl, sysval;
1445ddbe53jamie	int dflag, Rflag;
1456811668jamie#if defined(INET) || defined(INET6)
1466811668jamie	char *cs, *ncs;
1476811668jamie#endif
1486811668jamie#if defined(INET) && defined(INET6)
1496811668jamie	struct in6_addr addr6;
1506811668jamie#endif
151da4e70cbrooks
1523b31921jamie	op = 0;
1535ddbe53jamie	dflag = Rflag = 0;
1543b31921jamie	docf = 1;
1553b31921jamie	cfname = CONF_FILE;
1563b31921jamie	JidFile = NULL;
157da4e70cbrooks
1587e81b08eugen	while ((ch = getopt(argc, argv, "cde:f:hiJ:lmn:p:qrRs:u:U:v")) != -1) {
159723ed21maxim		switch (ch) {
1603b31921jamie		case 'c':
1613b31921jamie			op |= JF_START;
1623b31921jamie			break;
1638dbff96jamie		case 'd':
1643b31921jamie			dflag = 1;
1653b31921jamie			break;
1667e81b08eugen		case 'e':
1677e81b08eugen			op |= JF_SHOW;
1687e81b08eugen			separator = optarg;
1697e81b08eugen			break;
1703b31921jamie		case 'f':
1713b31921jamie			cfname = optarg;
1728dbff96jamie			break;
173d2730d5bz		case 'h':
1746811668jamie#if defined(INET) || defined(INET6)
175235aefejamie			add_param(NULL, NULL, IP_IP_HOSTNAME, NULL);
1766811668jamie#endif
1773b31921jamie			docf = 0;
178d2730d5bz			break;
1796067525mike		case 'i':
1806067525mike			iflag = 1;
1813b31921jamie			verbose = -1;
1826067525mike			break;
183865e779philip		case 'J':
184865e779philip			JidFile = optarg;
1853b31921jamie			break;
1863b31921jamie		case 'l':
187235aefejamie			add_param(NULL, NULL, IP_EXEC_CLEAN, NULL);
1883b31921jamie			docf = 0;
1893b31921jamie			break;
1903b31921jamie		case 'm':
1913b31921jamie			op |= JF_SET;
192865e779philip			break;
193d2730d5bz		case 'n':
194235aefejamie			add_param(NULL, NULL, KP_NAME, optarg);
1953b31921jamie			docf = 0;
196d2730d5bz			break;
1973b31921jamie		case 'p':
1983a156b8jamie			paralimit = strtol(optarg, NULL, 10);
1993a156b8jamie			if (paralimit == 0)
2003a156b8jamie				paralimit = -1;
201e329909matteo			break;
2023b31921jamie		case 'q':
2033b31921jamie			verbose = -1;
204872614cmaxim			break;
2053b31921jamie		case 'r':
2063b31921jamie			op |= JF_STOP;
207723ed21maxim			break;
2083b31921jamie		case 'R':
2093b31921jamie			op |= JF_STOP;
2103b31921jamie			Rflag = 1;
2114c83768maxim			break;
2123b31921jamie		case 's':
213235aefejamie			add_param(NULL, NULL, KP_SECURELEVEL, optarg);
2143b31921jamie			docf = 0;
2158dbff96jamie			break;
2163b31921jamie		case 'u':
217235aefejamie			add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg);
218235aefejamie			add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER, NULL);
2193b31921jamie			docf = 0;
2208dbff96jamie			break;
2213b31921jamie		case 'U':
222235aefejamie			add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg);
223235aefejamie			add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER,
224235aefejamie			    "false");
2253b31921jamie			docf = 0;
2263b31921jamie			break;
2273b31921jamie		case 'v':
2283b31921jamie			verbose = 1;
2298dbff96jamie			break;
230723ed21maxim		default:
231723ed21maxim			usage();
232723ed21maxim		}
2336067525mike	}
234723ed21maxim	argc -= optind;
235723ed21maxim	argv += optind;
2362c33480bz
2373b31921jamie	/* Find out which of the four command line styles this is. */
2383b31921jamie	oldcl = 0;
2393b31921jamie	if (!op) {
2403b31921jamie		/* Old-style command line with four fixed parameters */
2413b31921jamie		if (argc < 4 || argv[0][0] != '/')
2428dbff96jamie			usage();
2433b31921jamie		op = JF_START;
2443b31921jamie		docf = 0;
2453b31921jamie		oldcl = 1;
246235aefejamie		add_param(NULL, NULL, KP_PATH, argv[0]);
247235aefejamie		add_param(NULL, NULL, KP_HOST_HOSTNAME, argv[1]);
2486811668jamie#if defined(INET) || defined(INET6)
2493b31921jamie		if (argv[2][0] != '\0') {
2503b31921jamie			for (cs = argv[2];; cs = ncs + 1) {
2513b31921jamie				ncs = strchr(cs, ',');
2523b31921jamie				if (ncs)
2533b31921jamie					*ncs = '\0';
2543b31921jamie				add_param(NULL, NULL,
2556811668jamie#if defined(INET) && defined(INET6)
2563b31921jamie				    inet_pton(AF_INET6, cs, &addr6) == 1
2576811668jamie				    ? KP_IP6_ADDR : KP_IP4_ADDR,
2586811668jamie#elif defined(INET)
2596811668jamie				    KP_IP4_ADDR,
2606811668jamie#elif defined(INET6)
2616811668jamie				    KP_IP6_ADDR,
2622c33480bz#endif
2636811668jamie				    cs);
2643b31921jamie				if (!ncs)
2658dbff96jamie					break;
2668dbff96jamie			}
2678dbff96jamie		}
2688dbff96jamie#endif
2693b31921jamie		for (i = 3; i < argc; i++)
270235aefejamie			add_param(NULL, NULL, IP_COMMAND, argv[i]);
2713b31921jamie		/* Emulate the defaults from security.jail.* sysctls. */
27242b3c32jamie		sysvallen = sizeof(sysval);
27342b3c32jamie		if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
27442b3c32jamie		    NULL, 0) == 0 && sysval == 0) {
27542b3c32jamie			for (pi = 0; pi < sizeof(perm_sysctl) /
27642b3c32jamie			     sizeof(perm_sysctl[0]); pi++) {
27742b3c32jamie				sysvallen = sizeof(sysval);
278235aefejamie				if (sysctlbyname(perm_sysctl[pi].name,
27942b3c32jamie				    &sysval, &sysvallen, NULL, 0) == 0)
2803b31921jamie					add_param(NULL, NULL,
281235aefejamie					    perm_sysctl[pi].ipnum,
2828d19ad1emaste					    (sysval ? 1 : 0) ^
283235aefejamie					    perm_sysctl[pi].rev
284235aefejamie					    ? NULL : "false");
28542b3c32jamie			}
28642b3c32jamie		}
2877e81b08eugen	} else if (op == JF_STOP || op == JF_SHOW) {
2887e81b08eugen		/* Just print list of all configured non-wildcard jails */
2897e81b08eugen		if (op == JF_SHOW) {
2907e81b08eugen			load_config();
2917e81b08eugen			show_jails();
2927e81b08eugen			exit(0);
2937e81b08eugen		}
2943b31921jamie		/* Jail remove, perhaps using the config file */
2953b31921jamie		if (!docf || argc == 0)
2963b31921jamie			usage();
2973b31921jamie		if (!Rflag)
2983b31921jamie			for (i = 0; i < argc; i++)
2993b31921jamie				if (strchr(argv[i], '='))
3003b31921jamie					usage();
3013b31921jamie		if ((docf = !Rflag &&
3023b31921jamie		     (!strcmp(cfname, "-") || stat(cfname, &st) == 0)))
3033b31921jamie			load_config();
304b3870a1jamie		note_remove = docf || argc > 1 || wild_jail_name(argv[0]);
3053b31921jamie	} else if (argc > 1 || (argc == 1 && strchr(argv[0], '='))) {
3063b31921jamie		/* Single jail specified on the command line */
3073b31921jamie		if (Rflag)
3083b31921jamie			usage();
3093b31921jamie		docf = 0;
3103b31921jamie		for (i = 0; i < argc; i++) {
3113b31921jamie			if (!strncmp(argv[i], "command", 7) &&
3123b31921jamie			    (argv[i][7] == '\0' || argv[i][7] == '=')) {
3133b31921jamie				if (argv[i][7]  == '=')
314235aefejamie					add_param(NULL, NULL, IP_COMMAND,
3153b31921jamie					    argv[i] + 8);
3163b31921jamie				for (i++; i < argc; i++)
317235aefejamie					add_param(NULL, NULL, IP_COMMAND,
3183b31921jamie					    argv[i]);
31942b3c32jamie			}
3205aeb73ajamie#ifdef INET
3215aeb73ajamie			else if (!strncmp(argv[i], "ip4.addr=", 9)) {
3225aeb73ajamie				for (cs = argv[i] + 9;; cs = ncs + 1) {
3235aeb73ajamie					ncs = strchr(cs, ',');
3245aeb73ajamie					if (ncs)
3255aeb73ajamie						*ncs = '\0';
3265aeb73ajamie					add_param(NULL, NULL, KP_IP4_ADDR, cs);
3275aeb73ajamie					if (!ncs)
3285aeb73ajamie						break;
3295aeb73ajamie				}
3305aeb73ajamie			}
3315aeb73ajamie#endif
3325aeb73ajamie#ifdef INET6
3335aeb73ajamie			else if (!strncmp(argv[i], "ip6.addr=", 9)) {
3345aeb73ajamie				for (cs = argv[i] + 9;; cs = ncs + 1) {
3355aeb73ajamie					ncs = strchr(cs, ',');
3365aeb73ajamie					if (ncs)
3375aeb73ajamie						*ncs = '\0';
3385aeb73ajamie					add_param(NULL, NULL, KP_IP6_ADDR, cs);
3395aeb73ajamie					if (!ncs)
3405aeb73ajamie						break;
3415aeb73ajamie				}
3425aeb73ajamie			}
3435aeb73ajamie#endif
3445aeb73ajamie			else
3455aeb73ajamie				add_param(NULL, NULL, 0, argv[i]);
34642b3c32jamie		}
3473b31921jamie	} else {
3483b31921jamie		/* From the config file, perhaps with a specified jail */
3493b31921jamie		if (Rflag || !docf)
3503b31921jamie			usage();
3513b31921jamie		load_config();
352d2730d5bz	}
353d2730d5bz
3543b31921jamie	/* Find out which jails will be run. */
3553b31921jamie	dep_setup(docf);
3563b31921jamie	error = 0;
3573b31921jamie	if (op == JF_STOP) {
3583b31921jamie		for (i = 0; i < argc; i++)
359a85d762jamie			if (start_state(argv[i], docf, op, Rflag) < 0)
3603b31921jamie				error = 1;
3613b31921jamie	} else {
362a85d762jamie		if (start_state(argv[0], docf, op, 0) < 0)
3633b31921jamie			exit(1);
364865e779philip	}
3653b31921jamie
3663b31921jamie	jfp = NULL;
3673b31921jamie	if (JidFile != NULL) {
3683b31921jamie		jfp = fopen(JidFile, "w");
3693b31921jamie		if (jfp == NULL)
3703b31921jamie			err(1, "open %s", JidFile);
3713b31921jamie		setlinebuf(jfp);
37282a28cemike	}
3733b31921jamie	setlinebuf(stdout);
3743b31921jamie
3753b31921jamie	/*
3763b31921jamie	 * The main loop: Get an available jail and perform the required
3773b31921jamie	 * operation on it.  When that is done, the jail may be finished,
3783b31921jamie	 * or it may go back for the next step.
3793b31921jamie	 */
3803b31921jamie	while ((j = next_jail()))
3813b31921jamie	{
3823b31921jamie		if (j->flags & JF_FAILED) {
3833b31921jamie			error = 1;
384bf5da84jamie			if (j->comparam == NULL) {
385bf5da84jamie				dep_done(j, 0);
386bf5da84jamie				continue;
387bf5da84jamie			}
3883b31921jamie		}
38994aa5f7jamie		if (!(j->flags & JF_PARAMS))
3903b31921jamie		{
39194aa5f7jamie			j->flags |= JF_PARAMS;
3923b31921jamie			if (dflag)
393235aefejamie				add_param(j, NULL, IP_ALLOW_DYING, NULL);
3943b31921jamie			if (check_intparams(j) < 0)
3953b31921jamie				continue;
39694aa5f7jamie			if ((j->flags & (JF_START | JF_SET)) &&
39794aa5f7jamie			    import_params(j) < 0)
3983b31921jamie				continue;
3993b31921jamie		}
4003b31921jamie		if (!j->jid)
4013b31921jamie			running_jid(j,
4023b31921jamie			    (j->flags & (JF_SET | JF_DEPEND)) == JF_SET
4033b31921jamie			    ? dflag || bool_param(j->intparams[IP_ALLOW_DYING])
4043b31921jamie			    : 0);
405bf5da84jamie		if (finish_command(j))
4063b31921jamie			continue;
4073b31921jamie
4083b31921jamie		switch (j->flags & JF_OP_MASK) {
4093b31921jamie			/*
4103b31921jamie			 * These operations just turn into a different op
4113b31921jamie			 * depending on the jail's current status.
4123b31921jamie			 */
4133b31921jamie		case JF_START_SET:
4143b31921jamie			j->flags = j->jid < 0 ? JF_START : JF_SET;
4153b31921jamie			break;
4163b31921jamie		case JF_SET_RESTART:
4173b31921jamie			if (j->jid < 0) {
418a85d762jamie				jail_quoted_warnx(j, "not found",
419a85d762jamie				    "no jail specified");
4203b31921jamie				failed(j);
4213b31921jamie				continue;
4223b31921jamie			}
4233b31921jamie			j->flags = rdtun_params(j, 0) ? JF_RESTART : JF_SET;
4243b31921jamie			if (j->flags == JF_RESTART)
4253b31921jamie				dep_reset(j);
4263b31921jamie			break;
4273b31921jamie		case JF_START_SET_RESTART:
4283b31921jamie			j->flags = j->jid < 0 ? JF_START
4293b31921jamie			    : rdtun_params(j, 0) ? JF_RESTART : JF_SET;
4303b31921jamie			if (j->flags == JF_RESTART)
4313b31921jamie				dep_reset(j);
4323b31921jamie		}
4333b31921jamie
4343b31921jamie		switch (j->flags & JF_OP_MASK) {
4353b31921jamie		case JF_START:
436bf5da84jamie			if (j->comparam == NULL) {
4373b31921jamie				if (j->jid > 0 &&
4383b31921jamie				    !(j->flags & (JF_DEPEND | JF_WILD))) {
439a85d762jamie					jail_quoted_warnx(j, "already exists",
440a85d762jamie					    NULL);
4413b31921jamie					failed(j);
4423b31921jamie					continue;
4438dbff96jamie				}
4443b31921jamie				if (dep_check(j))
4453b31921jamie					continue;
4463b31921jamie				if (j->jid > 0)
4473b31921jamie					goto jail_create_done;
448416d58fjamie				j->comparam = startcommands;
449416d58fjamie				j->comstring = NULL;
4503b31921jamie			}
451bf5da84jamie			if (next_command(j))
452bf5da84jamie				continue;
453bf5da84jamie		jail_create_done:
454bf5da84jamie			clear_persist(j);
455bf5da84jamie			if (jfp != NULL)
4567e81b08eugen				print_jail(jfp, j, oldcl, 1);
457bf5da84jamie			dep_done(j, 0);
4583b31921jamie			break;
4593b31921jamie
4603b31921jamie		case JF_SET:
4613b31921jamie			if (j->jid < 0 && !(j->flags & JF_DEPEND)) {
462a85d762jamie				jail_quoted_warnx(j, "not found",
463a85d762jamie				    "no jail specified");
4643b31921jamie				failed(j);
4658d425bfjamie				continue;
4663b31921jamie			}
4673b31921jamie			if (dep_check(j))
4683b31921jamie				continue;
4693b31921jamie			if (!(j->flags & JF_DEPEND)) {
4703b31921jamie				if (rdtun_params(j, 1) < 0 ||
4713b31921jamie				    update_jail(j) < 0)
4723b31921jamie					continue;
4733b31921jamie				if (verbose >= 0 && (j->name || verbose > 0))
4743b31921jamie					jail_note(j, "updated\n");
4753b31921jamie			}
4763b31921jamie			dep_done(j, 0);
4773b31921jamie			break;
4783b31921jamie
4793b31921jamie		case JF_STOP:
4803b31921jamie		case JF_RESTART:
481bf5da84jamie			if (j->comparam == NULL) {
4823b31921jamie				if (dep_check(j))
4833b31921jamie					continue;
4843b31921jamie				if (j->jid < 0) {
485f8b6171hrs					if (!(j->flags & (JF_DEPEND|JF_WILD))) {
486f8b6171hrs						if (verbose >= 0)
487f8b6171hrs							jail_quoted_warnx(j,
488f8b6171hrs							    "not found", NULL);
489f8b6171hrs						failed(j);
490f8b6171hrs					}
4913b31921jamie					goto jail_remove_done;
4923b31921jamie				}
493416d58fjamie				j->comparam = stopcommands;
494416d58fjamie				j->comstring = NULL;
495b3870a1jamie			} else if ((j->flags & JF_FAILED) && j->jid > 0)
496bf5da84jamie				goto jail_remove_done;
497bf5da84jamie			if (next_command(j))
498bf5da84jamie				continue;
499bf5da84jamie		jail_remove_done:
500bf5da84jamie			dep_done(j, 0);
501bf5da84jamie			if ((j->flags & (JF_START | JF_FAILED)) == JF_START) {
502bf5da84jamie				j->comparam = NULL;
503bf5da84jamie				j->flags &= ~JF_STOP;
504bf5da84jamie				dep_reset(j);
505bf5da84jamie				requeue(j, j->ndeps ? &depend : &ready);
5063b31921jamie			}
5073b31921jamie			break;
5083b31921jamie		}
5093b31921jamie	}
5103b31921jamie
5113b31921jamie	if (jfp != NULL)
5123b31921jamie		fclose(jfp);
5133b31921jamie	exit(error);
5143b31921jamie}
5153b31921jamie
5163b31921jamie/*
5173b31921jamie * Mark a jail's failure for future handling.
5183b31921jamie */
5193b31921jamievoid
5203b31921jamiefailed(struct cfjail *j)
5213b31921jamie{
5223b31921jamie	j->flags |= JF_FAILED;
5233b31921jamie	TAILQ_REMOVE(j->queue, j, tq);
5243b31921jamie	TAILQ_INSERT_HEAD(&ready, j, tq);
5253b31921jamie	j->queue = &ready;
5263b31921jamie}
5273b31921jamie
5283b31921jamie/*
5293b31921jamie * Exit slightly more gracefully when out of memory.
5303b31921jamie */
5313b31921jamievoid *
5323b31921jamieemalloc(size_t size)
5333b31921jamie{
5343b31921jamie	void *p;
5353b31921jamie
5363b31921jamie	p = malloc(size);
5373b31921jamie	if (!p)
5383b31921jamie		err(1, "malloc");
5393b31921jamie	return p;
5403b31921jamie}
5413b31921jamie
5423b31921jamievoid *
5433b31921jamieerealloc(void *ptr, size_t size)
5443b31921jamie{
5453b31921jamie	void *p;
5463b31921jamie
5473b31921jamie	p = realloc(ptr, size);
5483b31921jamie	if (!p)
5493b31921jamie		err(1, "malloc");
5503b31921jamie	return p;
5513b31921jamie}
5523b31921jamie
5533b31921jamiechar *
5543b31921jamieestrdup(const char *str)
5553b31921jamie{
5563b31921jamie	char *ns;
5573b31921jamie
5583b31921jamie	ns = strdup(str);
5593b31921jamie	if (!ns)
5603b31921jamie		err(1, "malloc");
5613b31921jamie	return ns;
5623b31921jamie}
5633b31921jamie
5643b31921jamie/*
5653b31921jamie * Print a message including an optional jail name.
5663b31921jamie */
5673b31921jamievoid
5683b31921jamiejail_note(const struct cfjail *j, const char *fmt, ...)
5693b31921jamie{
5703b31921jamie	va_list ap, tap;
5713b31921jamie	char *cs;
5723b31921jamie	size_t len;
5733b31921jamie
5743b31921jamie	va_start(ap, fmt);
5753b31921jamie	va_copy(tap, ap);
5763b31921jamie	len = vsnprintf(NULL, 0, fmt, tap);
5773b31921jamie	va_end(tap);
5783b31921jamie	cs = alloca(len + 1);
5793b31921jamie	(void)vsnprintf(cs, len + 1, fmt, ap);
5803b31921jamie	va_end(ap);
5813b31921jamie	if (j->name)
5823b31921jamie		printf("%s: %s", j->name, cs);
5833b31921jamie	else
5843b31921jamie		printf("%s", cs);
5853b31921jamie}
5863b31921jamie
5873b31921jamie/*
5883b31921jamie * Print a warning message including an optional jail name.
5893b31921jamie */
5903b31921jamievoid
5913b31921jamiejail_warnx(const struct cfjail *j, const char *fmt, ...)
5923b31921jamie{
5933b31921jamie	va_list ap, tap;
5943b31921jamie	char *cs;
5953b31921jamie	size_t len;
5963b31921jamie
5973b31921jamie	va_start(ap, fmt);
5983b31921jamie	va_copy(tap, ap);
5993b31921jamie	len = vsnprintf(NULL, 0, fmt, tap);
6003b31921jamie	va_end(tap);
6013b31921jamie	cs = alloca(len + 1);
6023b31921jamie	(void)vsnprintf(cs, len + 1, fmt, ap);
6033b31921jamie	va_end(ap);
6043b31921jamie	if (j->name)
6053b31921jamie		warnx("%s: %s", j->name, cs);
6063b31921jamie	else
6073b31921jamie		warnx("%s", cs);
6083b31921jamie}
6093b31921jamie
6103b31921jamie/*
6113b31921jamie * Create a new jail.
6123b31921jamie */
613b3870a1jamieint
6143b31921jamiecreate_jail(struct cfjail *j)
6153b31921jamie{
6163b31921jamie	struct iovec jiov[4];
6173b31921jamie	struct stat st;
6183b31921jamie	struct jailparam *jp, *setparams, *setparams2, *sjp;
6193b31921jamie	const char *path;
6203b31921jamie	int dopersist, ns, jid, dying, didfail;
6213b31921jamie
6223b31921jamie	/*
6233b31921jamie	 * Check the jail's path, with a better error message than jail_set
6243b31921jamie	 * gives.
6253b31921jamie	 */
6263b31921jamie	if ((path = string_param(j->intparams[KP_PATH]))) {
6277fb1cfcjamie		if (j->name != NULL && path[0] != '/') {
628a7a7f45jamie			jail_warnx(j, "path %s: not an absolute pathname",
629a7a7f45jamie			    path);
630a7a7f45jamie			return -1;
631a7a7f45jamie		}
6323b31921jamie		if (stat(path, &st) < 0) {
6333b31921jamie			jail_warnx(j, "path %s: %s", path, strerror(errno));
6343b31921jamie			return -1;
635865e779philip		}
6363b31921jamie		if (!S_ISDIR(st.st_mode)) {
6373b31921jamie			jail_warnx(j, "path %s: %s", path, strerror(ENOTDIR));
6383b31921jamie			return -1;
639865e779philip		}
640865e779philip	}
6413b31921jamie
6423b31921jamie	/*
6433b31921jamie	 * Copy all the parameters, except that "persist" is always set when
6443b31921jamie	 * there are commands to run later.
6453b31921jamie	 */
6463b31921jamie	dopersist = !bool_param(j->intparams[KP_PERSIST]) &&
6473b31921jamie	    (j->intparams[IP_EXEC_START] || j->intparams[IP_COMMAND] ||
6483b31921jamie	     j->intparams[IP_EXEC_POSTSTART]);
6493b31921jamie	sjp = setparams =
6503b31921jamie	    alloca((j->njp + dopersist) * sizeof(struct jailparam));
6513b31921jamie	if (dopersist && jailparam_init(sjp++, "persist") < 0) {
6523b31921jamie		jail_warnx(j, "%s", jail_errmsg);
6533b31921jamie		return -1;
654865e779philip	}
6553b31921jamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
6563b31921jamie		if (!dopersist || !equalopts(jp->jp_name, "persist"))
6573b31921jamie			*sjp++ = *jp;
6583b31921jamie	ns = sjp - setparams;
6593b31921jamie
6603b31921jamie	didfail = 0;
6613b31921jamie	j->jid = jailparam_set_note(j, setparams, ns, JAIL_CREATE);
6623b31921jamie	if (j->jid < 0 && errno == EEXIST &&
6633b31921jamie	    bool_param(j->intparams[IP_ALLOW_DYING]) &&
6643b31921jamie	    int_param(j->intparams[KP_JID], &jid) && jid != 0) {
6653b31921jamie		/*
6663b31921jamie		 * The jail already exists, but may be dying.
6673b31921jamie		 * Make sure it is, in which case an update is appropriate.
6683b31921jamie		 */
6698665489jamie		jiov[0].iov_base = __DECONST(char *, "jid");
6703b31921jamie		jiov[0].iov_len = sizeof("jid");
6713b31921jamie		jiov[1].iov_base = &jid;
6723b31921jamie		jiov[1].iov_len = sizeof(jid);
6738665489jamie		jiov[2].iov_base = __DECONST(char *, "dying");
6743b31921jamie		jiov[2].iov_len = sizeof("dying");
6753b31921jamie		jiov[3].iov_base = &dying;
6763b31921jamie		jiov[3].iov_len = sizeof(dying);
6773b31921jamie		if (jail_get(jiov, 4, JAIL_DYING) < 0) {
6783b31921jamie			/*
6793b31921jamie			 * It could be that the jail just barely finished
6803b31921jamie			 * dying, or it could be that the jid never existed
6813b31921jamie			 * but the name does.  In either case, another try
6823b31921jamie			 * at creating the jail should do the right thing.
6833b31921jamie			 */
6843b31921jamie			if (errno == ENOENT)
6853b31921jamie				j->jid = jailparam_set_note(j, setparams, ns,
6863b31921jamie				    JAIL_CREATE);
6873b31921jamie		} else if (dying) {
6883b31921jamie			j->jid = jid;
6893b31921jamie			if (rdtun_params(j, 1) < 0) {
6903b31921jamie				j->jid = -1;
6913b31921jamie				didfail = 1;
6923b31921jamie			} else {
6933b31921jamie				sjp = setparams2 = alloca((j->njp + dopersist) *
6943b31921jamie				    sizeof(struct jailparam));
6953b31921jamie				for (jp = setparams; jp < setparams + ns; jp++)
6963b31921jamie					if (!JP_RDTUN(jp) ||
6973b31921jamie					    !strcmp(jp->jp_name, "jid"))
6983b31921jamie						*sjp++ = *jp;
6993b31921jamie				j->jid = jailparam_set_note(j, setparams2,
7003b31921jamie				    sjp - setparams2, JAIL_UPDATE | JAIL_DYING);
7013b31921jamie				/*
7023b31921jamie				 * Again, perhaps the jail just finished dying.
7033b31921jamie				 */
7043b31921jamie				if (j->jid < 0 && errno == ENOENT)
7053b31921jamie					j->jid = jailparam_set_note(j,
7063b31921jamie					    setparams, ns, JAIL_CREATE);
7073b31921jamie			}
7084c83768maxim		}
709723ed21maxim	}
7103b31921jamie	if (j->jid < 0 && !didfail) {
7113b31921jamie		jail_warnx(j, "%s", jail_errmsg);
7123b31921jamie		failed(j);
7134c83768maxim	}
7143b31921jamie	if (dopersist) {
7153b31921jamie		jailparam_free(setparams, 1);
7163b31921jamie		if (j->jid > 0)
7173b31921jamie			j->flags |= JF_PERSIST;
7184c83768maxim	}
7193b31921jamie	return j->jid;
720ca21a25phk}
721723ed21maxim
7223b31921jamie/*
7233b31921jamie * Remove a temporarily set "persist" parameter.
7243b31921jamie */
725723ed21maximstatic void
7263b31921jamieclear_persist(struct cfjail *j)
727723ed21maxim{
7283b31921jamie	struct iovec jiov[4];
7293b31921jamie	int jid;
7303b31921jamie
7313b31921jamie	if (!(j->flags & JF_PERSIST))
7323b31921jamie		return;
7333b31921jamie	j->flags &= ~JF_PERSIST;
7348665489jamie	jiov[0].iov_base = __DECONST(char *, "jid");
7353b31921jamie	jiov[0].iov_len = sizeof("jid");
7363b31921jamie	jiov[1].iov_base = &j->jid;
7373b31921jamie	jiov[1].iov_len = sizeof(j->jid);
7388665489jamie	jiov[2].iov_base = __DECONST(char *, "nopersist");
7393b31921jamie	jiov[2].iov_len = sizeof("nopersist");
7403b31921jamie	jiov[3].iov_base = NULL;
7413b31921jamie	jiov[3].iov_len = 0;
7423b31921jamie	jid = jail_set(jiov, 4, JAIL_UPDATE);
7433b31921jamie	if (verbose > 0)
7443b31921jamie		jail_note(j, "jail_set(JAIL_UPDATE) jid=%d nopersist%s%s\n",
7453b31921jamie		    j->jid, jid < 0 ? ": " : "",
7463b31921jamie		    jid < 0 ? strerror(errno) : "");
7473b31921jamie}
7483b31921jamie
7493b31921jamie/*
7503b31921jamie * Set a jail's parameters.
7513b31921jamie */
7523b31921jamiestatic int
7533b31921jamieupdate_jail(struct cfjail *j)
7543b31921jamie{
7553b31921jamie	struct jailparam *jp, *setparams, *sjp;
7563b31921jamie	int ns, jid;
7573b31921jamie
7583b31921jamie	ns = 0;
7593b31921jamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
7603b31921jamie		if (!JP_RDTUN(jp))
7613b31921jamie			ns++;
7623b31921jamie	if (ns == 0)
7633b31921jamie		return 0;
7643b31921jamie	sjp = setparams = alloca(++ns * sizeof(struct jailparam));
7653b31921jamie	if (jailparam_init(sjp, "jid") < 0 ||
7663b31921jamie	    jailparam_import_raw(sjp, &j->jid, sizeof j->jid) < 0) {
7673b31921jamie		jail_warnx(j, "%s", jail_errmsg);
7683b31921jamie		failed(j);
7693b31921jamie		return -1;
7708dbff96jamie	}
7713b31921jamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
7723b31921jamie		if (!JP_RDTUN(jp))
7733b31921jamie			*++sjp = *jp;
7743b31921jamie
7753b31921jamie	jid = jailparam_set_note(j, setparams, ns,
7763b31921jamie	    bool_param(j->intparams[IP_ALLOW_DYING])
7773b31921jamie	    ? JAIL_UPDATE | JAIL_DYING : JAIL_UPDATE);
7783b31921jamie	if (jid < 0) {
7793b31921jamie		jail_warnx(j, "%s", jail_errmsg);
7803b31921jamie		failed(j);
7813b31921jamie	}
7823b31921jamie	jailparam_free(setparams, 1);
7833b31921jamie	return jid;
7848dbff96jamie}
785723ed21maxim
7863b31921jamie/*
7873b31921jamie * Return if a jail set would change any create-only parameters.
7883b31921jamie */
7893b31921jamiestatic int
7903b31921jamierdtun_params(struct cfjail *j, int dofail)
791723ed21maxim{
7923b31921jamie	struct jailparam *jp, *rtparams, *rtjp;
7933b31921jamie	int nrt, rval;
7943b31921jamie
7953b31921jamie	if (j->flags & JF_RDTUN)
7963b31921jamie		return 0;
7973b31921jamie	j->flags |= JF_RDTUN;
7983b31921jamie	nrt = 0;
7993b31921jamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
8003b31921jamie		if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid"))
8013b31921jamie			nrt++;
8023b31921jamie	if (nrt == 0)
8033b31921jamie		return 0;
8043b31921jamie	rtjp = rtparams = alloca(++nrt * sizeof(struct jailparam));
8053b31921jamie	if (jailparam_init(rtjp, "jid") < 0 ||
8063b31921jamie	    jailparam_import_raw(rtjp, &j->jid, sizeof j->jid) < 0) {
8073b31921jamie		jail_warnx(j, "%s", jail_errmsg);
8083b31921jamie		exit(1);
8098dbff96jamie	}
8103b31921jamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
811944c2d4jamie		if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) {
8123b31921jamie			*++rtjp = *jp;
813944c2d4jamie			rtjp->jp_value = NULL;
814944c2d4jamie		}
8153b31921jamie	rval = 0;
8163b31921jamie	if (jailparam_get(rtparams, nrt,
8173b31921jamie	    bool_param(j->intparams[IP_ALLOW_DYING]) ? JAIL_DYING : 0) > 0) {
8183b31921jamie		rtjp = rtparams + 1;
8190809c4bdelphij		for (jp = j->jp; rtjp < rtparams + nrt; jp++) {
8203b31921jamie			if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) {
8213b31921jamie				if (!((jp->jp_flags & (JP_BOOL | JP_NOBOOL)) &&
8223b31921jamie				    jp->jp_valuelen == 0 &&
8233b31921jamie				    *(int *)jp->jp_value) &&
8243b31921jamie				    !(rtjp->jp_valuelen == jp->jp_valuelen &&
825944c2d4jamie				    !((jp->jp_ctltype & CTLTYPE) ==
826944c2d4jamie				    CTLTYPE_STRING ? strncmp(rtjp->jp_value,
827944c2d4jamie				    jp->jp_value, jp->jp_valuelen) :
828944c2d4jamie				    memcmp(rtjp->jp_value, jp->jp_value,
829944c2d4jamie				    jp->jp_valuelen)))) {
8303b31921jamie					if (dofail) {
8313b31921jamie						jail_warnx(j, "%s cannot be "
8323b31921jamie						    "changed after creation",
8333b31921jamie						    jp->jp_name);
8343b31921jamie						failed(j);
8353b31921jamie						rval = -1;
8363b31921jamie					} else
8373b31921jamie						rval = 1;
8383b31921jamie					break;
8393b31921jamie				}
8403b31921jamie				rtjp++;
8413b31921jamie			}
8423b31921jamie		}
8433b31921jamie	}
8443b31921jamie	for (rtjp = rtparams + 1; rtjp < rtparams + nrt; rtjp++)
8453b31921jamie		rtjp->jp_name = NULL;
8463b31921jamie	jailparam_free(rtparams, nrt);
8473b31921jamie	return rval;
8488dbff96jamie}
849723ed21maxim
8503b31921jamie/*
8513b31921jamie * Get the jail's jid if it is running.
8523b31921jamie */
8538dbff96jamiestatic void
8543b31921jamierunning_jid(struct cfjail *j, int dflag)
8558dbff96jamie{
8563b31921jamie	struct iovec jiov[2];
8573b31921jamie	const char *pval;
8583b31921jamie	char *ep;
8593b31921jamie	int jid;
8608dbff96jamie
8613c30caajamie	if ((pval = string_param(j->intparams[KP_JID]))) {
8623b31921jamie		if (!(jid = strtol(pval, &ep, 10)) || *ep) {
8633b31921jamie			j->jid = -1;
8643b31921jamie			return;
8653b31921jamie		}
8668665489jamie		jiov[0].iov_base = __DECONST(char *, "jid");
8673b31921jamie		jiov[0].iov_len = sizeof("jid");
8683b31921jamie		jiov[1].iov_base = &jid;
8693b31921jamie		jiov[1].iov_len = sizeof(jid);
8703c30caajamie	} else if ((pval = string_param(j->intparams[KP_NAME]))) {
8718665489jamie		jiov[0].iov_base = __DECONST(char *, "name");
8723b31921jamie		jiov[0].iov_len = sizeof("name");
8733b31921jamie		jiov[1].iov_len = strlen(pval) + 1;
8743b31921jamie		jiov[1].iov_base = alloca(jiov[1].iov_len);
8753b31921jamie		strcpy(jiov[1].iov_base, pval);
8763c30caajamie	} else {
8773c30caajamie		j->jid = -1;
8783c30caajamie		return;
8798dbff96jamie	}
8803b31921jamie	j->jid = jail_get(jiov, 2, dflag ? JAIL_DYING : 0);
881723ed21maxim}
8828dbff96jamie
883a85d762jamiestatic void
884a85d762jamiejail_quoted_warnx(const struct cfjail *j, const char *name_msg,
885a85d762jamie    const char *noname_msg)
886a85d762jamie{
887a85d762jamie	const char *pval;
888a85d762jamie
889a85d762jamie	if ((pval = j->name) || (pval = string_param(j->intparams[KP_JID])) ||
890a85d762jamie	    (pval = string_param(j->intparams[KP_NAME])))
891a85d762jamie		warnx("\"%s\" %s", pval, name_msg);
892a85d762jamie	else
893a85d762jamie		warnx("%s", noname_msg);
894