13b31921jamie/*-
27551d83pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
37551d83pfg *
48d425bfjamie * Copyright (c) 2011 James Gritton
53b31921jamie * All rights reserved.
63b31921jamie *
73b31921jamie * Redistribution and use in source and binary forms, with or without
83b31921jamie * modification, are permitted provided that the following conditions
93b31921jamie * are met:
103b31921jamie * 1. Redistributions of source code must retain the above copyright
113b31921jamie *    notice, this list of conditions and the following disclaimer.
123b31921jamie * 2. Redistributions in binary form must reproduce the above copyright
133b31921jamie *    notice, this list of conditions and the following disclaimer in the
143b31921jamie *    documentation and/or other materials provided with the distribution.
153b31921jamie *
163b31921jamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
173b31921jamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
183b31921jamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
193b31921jamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
203b31921jamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
213b31921jamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
223b31921jamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
233b31921jamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
243b31921jamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
253b31921jamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
263b31921jamie * SUCH DAMAGE.
273b31921jamie */
283b31921jamie
293b31921jamie#include <sys/cdefs.h>
303b31921jamie__FBSDID("$FreeBSD$");
313b31921jamie
323b31921jamie#include <sys/types.h>
338576789jamie#include <sys/errno.h>
343b31921jamie#include <sys/socket.h>
353b31921jamie#include <sys/sysctl.h>
363b31921jamie
373b31921jamie#include <arpa/inet.h>
383b31921jamie#include <netinet/in.h>
393b31921jamie
403b31921jamie#include <err.h>
413b31921jamie#include <netdb.h>
423b31921jamie#include <stdio.h>
433b31921jamie#include <stdlib.h>
443b31921jamie#include <string.h>
456811668jamie#include <unistd.h>
463b31921jamie
473b31921jamie#include "jailp.h"
483b31921jamie
493b31921jamiestruct ipspec {
503b31921jamie	const char	*name;
513b31921jamie	unsigned	flags;
523b31921jamie};
533b31921jamie
543b31921jamieextern FILE *yyin;
553b31921jamieextern int yynerrs;
563b31921jamie
57310ab6dbaptextern int yyparse(void);
58310ab6dbapt
593b31921jamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
603b31921jamie
613b31921jamiestatic void free_param(struct cfparams *pp, struct cfparam *p);
623b31921jamiestatic void free_param_strings(struct cfparam *p);
633b31921jamie
643b31921jamiestatic const struct ipspec intparams[] = {
65235aefejamie    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
66235aefejamie    [IP_COMMAND] =		{"command",		PF_INTERNAL},
67235aefejamie    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
68235aefejamie    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
69235aefejamie    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
70235aefejamie    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
71235aefejamie    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
72235aefejamie    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
73235aefejamie    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
74235aefejamie    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
75235aefejamie    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
7642ccecbnetchild    [IP_EXEC_CREATED] =		{"exec.created",	PF_INTERNAL},
77235aefejamie    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
78235aefejamie    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
79235aefejamie    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
80235aefejamie							PF_INTERNAL | PF_BOOL},
81235aefejamie    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
82235aefejamie    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
836811668jamie#if defined(INET) || defined(INET6)
84235aefejamie    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
85235aefejamie    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
866811668jamie#endif
8782d5811jamie    [IP_MOUNT] =		{"mount",		PF_INTERNAL | PF_REV},
88235aefejamie    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
89513bdd9hrs    [IP_MOUNT_FDESCFS] =	{"mount.fdescfs",	PF_INTERNAL | PF_BOOL},
906064614jamie    [IP_MOUNT_PROCFS] =		{"mount.procfs",	PF_INTERNAL | PF_BOOL},
91235aefejamie    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
92235aefejamie    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
93235aefejamie    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
946811668jamie#ifdef INET
9582d5811jamie    [IP__IP4_IFADDR] =		{"ip4.addr",	PF_INTERNAL | PF_CONV | PF_REV},
966811668jamie#endif
973b31921jamie#ifdef INET6
9882d5811jamie    [IP__IP6_IFADDR] =		{"ip6.addr",	PF_INTERNAL | PF_CONV | PF_REV},
993b31921jamie#endif
10082d5811jamie    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",	PF_INTERNAL | PF_CONV | PF_REV},
101bf5da84jamie    [IP__OP] =			{NULL,			PF_CONV},
102235aefejamie    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
103235aefejamie    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
104235aefejamie    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
105235aefejamie    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
106235aefejamie    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
107235aefejamie    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
1086fe59c6jamie    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
109235aefejamie    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
1106811668jamie#ifdef INET
111235aefejamie    [KP_IP4_ADDR] =		{"ip4.addr",		0},
1126811668jamie#endif
113235aefejamie#ifdef INET6
114235aefejamie    [KP_IP6_ADDR] =		{"ip6.addr",		0},
115235aefejamie#endif
1166a6f426hrs    [KP_JID] =			{"jid",			PF_IMMUTABLE},
1176a6f426hrs    [KP_NAME] =			{"name",		PF_IMMUTABLE},
118235aefejamie    [KP_PATH] =			{"path",		0},
119235aefejamie    [KP_PERSIST] =		{"persist",		0},
120235aefejamie    [KP_SECURELEVEL] =		{"securelevel",		0},
121235aefejamie    [KP_VNET] =			{"vnet",		0},
1223b31921jamie};
1233b31921jamie
1243b31921jamie/*
1253b31921jamie * Parse the jail configuration file.
1263b31921jamie */
1273b31921jamievoid
1283b31921jamieload_config(void)
1293b31921jamie{
1303b31921jamie	struct cfjails wild;
1313b31921jamie	struct cfparams opp;
1323b31921jamie	struct cfjail *j, *tj, *wj;
1333b31921jamie	struct cfparam *p, *vp, *tp;
1343b31921jamie	struct cfstring *s, *vs, *ns;
1353c6c216hrs	struct cfvar *v, *vv;
1363b31921jamie	char *ep;
1373b31921jamie	int did_self, jseq, pgen;
1383b31921jamie
1393b31921jamie	if (!strcmp(cfname, "-")) {
1403b31921jamie		cfname = "STDIN";
1413b31921jamie		yyin = stdin;
1423b31921jamie	} else {
1433b31921jamie		yyin = fopen(cfname, "r");
1443b31921jamie		if (!yyin)
1453b31921jamie			err(1, "%s", cfname);
1463b31921jamie	}
1473b31921jamie	if (yyparse() || yynerrs)
1483b31921jamie		exit(1);
1493b31921jamie
1503b31921jamie	/* Separate the wildcard jails out from the actual jails. */
1513b31921jamie	jseq = 0;
1523b31921jamie	TAILQ_INIT(&wild);
1533b31921jamie	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
1543b31921jamie		j->seq = ++jseq;
1553b31921jamie		if (wild_jail_name(j->name))
1563b31921jamie			requeue(j, &wild);
1573b31921jamie	}
1583b31921jamie
1593b31921jamie	TAILQ_FOREACH(j, &cfjails, tq) {
1603b31921jamie		/* Set aside the jail's parameters. */
1613b31921jamie		TAILQ_INIT(&opp);
1623b31921jamie		TAILQ_CONCAT(&opp, &j->params, tq);
1633b31921jamie		/*
1643b31921jamie		 * The jail name implies its "name" or "jid" parameter,
1653b31921jamie		 * though they may also be explicitly set later on.
1663b31921jamie		 */
1673b31921jamie		add_param(j, NULL,
168235aefejamie		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
1693b31921jamie		    j->name);
1703b31921jamie		/*
1713b31921jamie		 * Collect parameters for the jail, global parameters/variables,
1723b31921jamie		 * and any matching wildcard jails.
1733b31921jamie		 */
1743b31921jamie		did_self = 0;
1753b31921jamie		TAILQ_FOREACH(wj, &wild, tq) {
1763b31921jamie			if (j->seq < wj->seq && !did_self) {
1773b31921jamie				TAILQ_FOREACH(p, &opp, tq)
178235aefejamie					add_param(j, p, 0, NULL);
1793b31921jamie				did_self = 1;
1803b31921jamie			}
1813b31921jamie			if (wild_jail_match(j->name, wj->name))
1823b31921jamie				TAILQ_FOREACH(p, &wj->params, tq)
183235aefejamie					add_param(j, p, 0, NULL);
1843b31921jamie		}
1853b31921jamie		if (!did_self)
1863b31921jamie			TAILQ_FOREACH(p, &opp, tq)
187235aefejamie				add_param(j, p, 0, NULL);
1883b31921jamie
1893b31921jamie		/* Resolve any variable substitutions. */
1903b31921jamie		pgen = 0;
1913b31921jamie		TAILQ_FOREACH(p, &j->params, tq) {
1923b31921jamie		    p->gen = ++pgen;
1933b31921jamie		find_vars:
1940e5ec9djamie		    TAILQ_FOREACH(s, &p->val, tq) {
1953b31921jamie			while ((v = STAILQ_FIRST(&s->vars))) {
1963b31921jamie				TAILQ_FOREACH(vp, &j->params, tq)
1973b31921jamie					if (!strcmp(vp->name, v->name))
1983b31921jamie						break;
1993b31921jamie				if (!vp) {
2003b31921jamie					jail_warnx(j,
2013b31921jamie					    "%s: variable \"%s\" not found",
2023b31921jamie					    p->name, v->name);
2033b31921jamie				bad_var:
2043b31921jamie					j->flags |= JF_FAILED;
2053b31921jamie					TAILQ_FOREACH(vp, &j->params, tq)
2063b31921jamie						if (vp->gen == pgen)
2073b31921jamie							vp->flags |= PF_BAD;
2083b31921jamie					goto free_var;
2093b31921jamie				}
2103b31921jamie				if (vp->flags & PF_BAD)
2113b31921jamie					goto bad_var;
2123b31921jamie				if (vp->gen == pgen) {
2133b31921jamie					jail_warnx(j, "%s: variable loop",
2143b31921jamie					    v->name);
2153b31921jamie					goto bad_var;
2163b31921jamie				}
2170e5ec9djamie				TAILQ_FOREACH(vs, &vp->val, tq)
2183b31921jamie					if (!STAILQ_EMPTY(&vs->vars)) {
2193b31921jamie						vp->gen = pgen;
2203b31921jamie						TAILQ_REMOVE(&j->params, vp,
2213b31921jamie						    tq);
2223b31921jamie						TAILQ_INSERT_BEFORE(p, vp, tq);
2233b31921jamie						p = vp;
2243b31921jamie						goto find_vars;
2253b31921jamie					}
2260e5ec9djamie				vs = TAILQ_FIRST(&vp->val);
2270e5ec9djamie				if (TAILQ_NEXT(vs, tq) != NULL &&
2283b31921jamie				    (s->s[0] != '\0' ||
2293b31921jamie				     STAILQ_NEXT(v, tq))) {
2303b31921jamie					jail_warnx(j, "%s: array cannot be "
2313b31921jamie					    "substituted inline",
2323b31921jamie					    p->name);
2333b31921jamie					goto bad_var;
2343b31921jamie				}
2353b31921jamie				s->s = erealloc(s->s, s->len + vs->len + 1);
2363c6c216hrs				memmove(s->s + v->pos + vs->len,
2373c6c216hrs				    s->s + v->pos,
2383c6c216hrs				    s->len - v->pos + 1);
2393c6c216hrs				memcpy(s->s + v->pos, vs->s, vs->len);
2403c6c216hrs				vv = v;
2413c6c216hrs				while ((vv = STAILQ_NEXT(vv, tq)))
2423c6c216hrs					vv->pos += vs->len;
2433b31921jamie				s->len += vs->len;
2440e5ec9djamie				while ((vs = TAILQ_NEXT(vs, tq))) {
2453b31921jamie					ns = emalloc(sizeof(struct cfstring));
2463b31921jamie					ns->s = estrdup(vs->s);
2473b31921jamie					ns->len = vs->len;
2483b31921jamie					STAILQ_INIT(&ns->vars);
2490e5ec9djamie					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
2503b31921jamie					s = ns;
2513b31921jamie				}
2523b31921jamie			free_var:
2533b31921jamie				free(v->name);
2543b31921jamie				STAILQ_REMOVE_HEAD(&s->vars, tq);
2553b31921jamie				free(v);
2563b31921jamie			}
2573b31921jamie		    }
2583b31921jamie		}
2593b31921jamie
2603b31921jamie		/* Free the jail's original parameter list and any variables. */
2613b31921jamie		while ((p = TAILQ_FIRST(&opp)))
2623b31921jamie			free_param(&opp, p);
2633b31921jamie		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
2643b31921jamie			if (p->flags & PF_VAR)
2653b31921jamie				free_param(&j->params, p);
2663b31921jamie	}
2673b31921jamie	while ((wj = TAILQ_FIRST(&wild))) {
2683b31921jamie		free(wj->name);
2693b31921jamie		while ((p = TAILQ_FIRST(&wj->params)))
2703b31921jamie			free_param(&wj->params, p);
2713b31921jamie		TAILQ_REMOVE(&wild, wj, tq);
2723b31921jamie	}
2733b31921jamie}
2743b31921jamie
2753b31921jamie/*
2763b31921jamie * Create a new jail record.
2773b31921jamie */
2783b31921jamiestruct cfjail *
2793b31921jamieadd_jail(void)
2803b31921jamie{
2813b31921jamie	struct cfjail *j;
2823b31921jamie
2833b31921jamie	j = emalloc(sizeof(struct cfjail));
2843b31921jamie	memset(j, 0, sizeof(struct cfjail));
2853b31921jamie	TAILQ_INIT(&j->params);
2863b31921jamie	STAILQ_INIT(&j->dep[DEP_FROM]);
2873b31921jamie	STAILQ_INIT(&j->dep[DEP_TO]);
2883b31921jamie	j->queue = &cfjails;
2893b31921jamie	TAILQ_INSERT_TAIL(&cfjails, j, tq);
2903b31921jamie	return j;
2913b31921jamie}
2923b31921jamie
2933b31921jamie/*
2943b31921jamie * Add a parameter to a jail.
2953b31921jamie */
2963b31921jamievoid
297235aefejamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
2983b31921jamie    const char *value)
2993b31921jamie{
3003b31921jamie	struct cfstrings nss;
3013b31921jamie	struct cfparam *dp, *np;
3023b31921jamie	struct cfstring *s, *ns;
3033b31921jamie	struct cfvar *v, *nv;
304235aefejamie	const char *name;
305235aefejamie	char *cs, *tname;
3063b31921jamie	unsigned flags;
3073b31921jamie
3083b31921jamie	if (j == NULL) {
3093b31921jamie		/* Create a single anonymous jail if one doesn't yet exist. */
3103b31921jamie		j = TAILQ_LAST(&cfjails, cfjails);
3113b31921jamie		if (j == NULL)
3123b31921jamie			j = add_jail();
3133b31921jamie	}
3140e5ec9djamie	TAILQ_INIT(&nss);
3153b31921jamie	if (p != NULL) {
3163b31921jamie		name = p->name;
3173b31921jamie		flags = p->flags;
3183b31921jamie		/*
3193b31921jamie		 * Make a copy of the parameter's string list,
3203b31921jamie		 * which may be freed if it's overridden later.
3213b31921jamie		 */
3220e5ec9djamie		TAILQ_FOREACH(s, &p->val, tq) {
3233b31921jamie			ns = emalloc(sizeof(struct cfstring));
3243b31921jamie			ns->s = estrdup(s->s);
3253b31921jamie			ns->len = s->len;
3263b31921jamie			STAILQ_INIT(&ns->vars);
3273b31921jamie			STAILQ_FOREACH(v, &s->vars, tq) {
3283b31921jamie				nv = emalloc(sizeof(struct cfvar));
3293b31921jamie				nv->name = strdup(v->name);
3303b31921jamie				nv->pos = v->pos;
3313b31921jamie				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
3323b31921jamie			}
3330e5ec9djamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
3343b31921jamie		}
3353b31921jamie	} else {
3363b31921jamie		flags = PF_APPEND;
33718b00cejamie		if (ipnum != IP__NULL) {
338235aefejamie			name = intparams[ipnum].name;
339235aefejamie			flags |= intparams[ipnum].flags;
340235aefejamie		} else if ((cs = strchr(value, '='))) {
341235aefejamie			tname = alloca(cs - value + 1);
342235aefejamie			strlcpy(tname, value, cs - value + 1);
343235aefejamie			name = tname;
344235aefejamie			value = cs + 1;
345235aefejamie		} else {
346235aefejamie			name = value;
347235aefejamie			value = NULL;
348235aefejamie		}
3493b31921jamie		if (value != NULL) {
3503b31921jamie			ns = emalloc(sizeof(struct cfstring));
3513b31921jamie			ns->s = estrdup(value);
3523b31921jamie			ns->len = strlen(value);
3533b31921jamie			STAILQ_INIT(&ns->vars);
3540e5ec9djamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
3553b31921jamie		}
3563b31921jamie	}
3573b31921jamie
3583b31921jamie	/* See if this parameter has already been added. */
35918b00cejamie	if (ipnum != IP__NULL)
360235aefejamie		dp = j->intparams[ipnum];
361235aefejamie	else
362235aefejamie		TAILQ_FOREACH(dp, &j->params, tq)
363235aefejamie			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
364235aefejamie				break;
365235aefejamie	if (dp != NULL) {
366235aefejamie		/* Found it - append or replace. */
3676a6f426hrs		if (dp->flags & PF_IMMUTABLE) {
3686a6f426hrs			jail_warnx(j, "cannot redefine variable \"%s\".",
3696a6f426hrs			    dp->name);
3706a6f426hrs			return;
3716a6f426hrs		}
372235aefejamie		if (strcmp(dp->name, name)) {
373235aefejamie			free(dp->name);
374235aefejamie			dp->name = estrdup(name);
3753b31921jamie		}
3760e5ec9djamie		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
377235aefejamie			free_param_strings(dp);
3780e5ec9djamie		TAILQ_CONCAT(&dp->val, &nss, tq);
379235aefejamie		dp->flags |= flags;
380235aefejamie	} else {
3813b31921jamie		/* Not found - add it. */
3823b31921jamie		np = emalloc(sizeof(struct cfparam));
3833b31921jamie		np->name = estrdup(name);
3840e5ec9djamie		TAILQ_INIT(&np->val);
3850e5ec9djamie		TAILQ_CONCAT(&np->val, &nss, tq);
3863b31921jamie		np->flags = flags;
3873b31921jamie		np->gen = 0;
3883b31921jamie		TAILQ_INSERT_TAIL(&j->params, np, tq);
38918b00cejamie		if (ipnum != IP__NULL)
390235aefejamie			j->intparams[ipnum] = np;
391235aefejamie		else
39218b00cejamie			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
393235aefejamie				if (!(intparams[ipnum].flags & PF_CONV) &&
394235aefejamie				    equalopts(name, intparams[ipnum].name)) {
395235aefejamie					j->intparams[ipnum] = np;
396235aefejamie					np->flags |= intparams[ipnum].flags;
397235aefejamie					break;
398235aefejamie				}
3993b31921jamie	}
4003b31921jamie}
4013b31921jamie
4023b31921jamie/*
4033b31921jamie * Return if a boolean parameter exists and is true.
4043b31921jamie */
4053b31921jamieint
4063b31921jamiebool_param(const struct cfparam *p)
4073b31921jamie{
4083b31921jamie	const char *cs;
4093b31921jamie
4103b31921jamie	if (p == NULL)
4113b31921jamie		return 0;
4123b31921jamie	cs = strrchr(p->name, '.');
4133b31921jamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
4140e5ec9djamie	    (TAILQ_EMPTY(&p->val) ||
4150e5ec9djamie	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
4160e5ec9djamie	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
4173b31921jamie}
4183b31921jamie
4193b31921jamie/*
4203b31921jamie * Set an integer if a parameter if it exists.
4213b31921jamie */
4223b31921jamieint
4233b31921jamieint_param(const struct cfparam *p, int *ip)
4243b31921jamie{
4250e5ec9djamie	if (p == NULL || TAILQ_EMPTY(&p->val))
4263b31921jamie		return 0;
4270e5ec9djamie	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
4283b31921jamie	return 1;
4293b31921jamie}
4303b31921jamie
4313b31921jamie/*
4323b31921jamie * Return the string value of a scalar parameter if it exists.
4333b31921jamie */
4343b31921jamieconst char *
4353b31921jamiestring_param(const struct cfparam *p)
4363b31921jamie{
4370e5ec9djamie	return (p && !TAILQ_EMPTY(&p->val)
4380e5ec9djamie	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
4393b31921jamie}
4403b31921jamie
4413b31921jamie/*
44294aa5f7jamie * Check syntax and values of internal parameters.  Set some internal
44394aa5f7jamie * parameters based on the values of others.
4443b31921jamie */
4453b31921jamieint
44694aa5f7jamiecheck_intparams(struct cfjail *j)
4473b31921jamie{
44894aa5f7jamie	struct cfparam *p;
4496a72e94jamie	struct cfstring *s;
4508576789jamie	FILE *f;
4516811668jamie	const char *val;
4528576789jamie	char *cs, *ep, *ln;
4536811668jamie	size_t lnlen;
4546811668jamie	int error;
4556811668jamie#if defined(INET) || defined(INET6)
4566811668jamie	struct addrinfo hints;
4576811668jamie	struct addrinfo *ai0, *ai;
4586811668jamie	const char *hostname;
4592595bebjamie	int gicode, defif;
4606811668jamie#endif
4616811668jamie#ifdef INET
4626811668jamie	struct in_addr addr4;
4636811668jamie	int ip4ok;
4643b31921jamie	char avalue4[INET_ADDRSTRLEN];
4656811668jamie#endif
4663b31921jamie#ifdef INET6
4673b31921jamie	struct in6_addr addr6;
4686811668jamie	int ip6ok;
4693b31921jamie	char avalue6[INET6_ADDRSTRLEN];
4703b31921jamie#endif
4713b31921jamie
4723b31921jamie	error = 0;
47394aa5f7jamie	/* Check format of boolan and integer values. */
47494aa5f7jamie	TAILQ_FOREACH(p, &j->params, tq) {
4750e5ec9djamie		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
4760e5ec9djamie			val = TAILQ_LAST(&p->val, cfstrings)->s;
47794aa5f7jamie			if (p->flags & PF_BOOL) {
47894aa5f7jamie				if (strcasecmp(val, "false") &&
47994aa5f7jamie				    strcasecmp(val, "true") &&
48094aa5f7jamie				    ((void)strtol(val, &ep, 10), *ep)) {
48194aa5f7jamie					jail_warnx(j,
48294aa5f7jamie					    "%s: unknown boolean value \"%s\"",
48394aa5f7jamie					    p->name, val);
48494aa5f7jamie					error = -1;
48594aa5f7jamie				}
48694aa5f7jamie			} else {
48794aa5f7jamie				(void)strtol(val, &ep, 10);
48894aa5f7jamie				if (ep == val || *ep) {
48994aa5f7jamie					jail_warnx(j,
49094aa5f7jamie					    "%s: non-integer value \"%s\"",
49194aa5f7jamie					    p->name, val);
49294aa5f7jamie					error = -1;
49394aa5f7jamie				}
49494aa5f7jamie			}
49594aa5f7jamie		}
49694aa5f7jamie	}
49794aa5f7jamie
4986811668jamie#if defined(INET) || defined(INET6)
4993b31921jamie	/*
5003b31921jamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
5013b31921jamie	 * for any IP addresses it finds.
5023b31921jamie	 */
50394aa5f7jamie	if (((j->flags & JF_OP_MASK) != JF_STOP ||
50494aa5f7jamie	    j->intparams[IP_INTERFACE] != NULL) &&
50594aa5f7jamie	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
506235aefejamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
5073b31921jamie		j->intparams[IP_IP_HOSTNAME] = NULL;
5083b31921jamie		/*
5093b31921jamie		 * Silently ignore unsupported address families from
5103b31921jamie		 * DNS lookups.
5113b31921jamie		 */
5126811668jamie#ifdef INET
5136811668jamie		ip4ok = feature_present("inet");
5143b31921jamie#endif
5153b31921jamie#ifdef INET6
5166811668jamie		ip6ok = feature_present("inet6");
5176811668jamie#endif
5186811668jamie		if (
5196811668jamie#if defined(INET) && defined(INET6)
5206811668jamie		    ip4ok || ip6ok
5216811668jamie#elif defined(INET)
5226811668jamie		    ip4ok
5236811668jamie#elif defined(INET6)
5246811668jamie		    ip6ok
5253b31921jamie#endif
5266811668jamie			 ) {
5273b31921jamie			/* Look up the hostname (or get the address) */
5283b31921jamie			memset(&hints, 0, sizeof(hints));
5293b31921jamie			hints.ai_socktype = SOCK_STREAM;
5303b31921jamie			hints.ai_family =
5316811668jamie#if defined(INET) && defined(INET6)
5326811668jamie			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
5336811668jamie#elif defined(INET)
5343b31921jamie			    PF_INET;
5356811668jamie#elif defined(INET6)
5366811668jamie			    PF_INET6;
5376811668jamie#endif
53894aa5f7jamie			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
53994aa5f7jamie			if (gicode != 0) {
5403b31921jamie				jail_warnx(j, "host.hostname %s: %s", hostname,
54194aa5f7jamie				    gai_strerror(gicode));
5423b31921jamie				error = -1;
5433b31921jamie			} else {
5443b31921jamie				/*
5453b31921jamie				 * Convert the addresses to ASCII so jailparam
5463b31921jamie				 * can convert them back.  Errors are not
5473b31921jamie				 * expected here.
5483b31921jamie				 */
5493b31921jamie				for (ai = ai0; ai; ai = ai->ai_next)
5503b31921jamie					switch (ai->ai_family) {
5516811668jamie#ifdef INET
5523b31921jamie					case AF_INET:
5533b31921jamie						memcpy(&addr4,
5543b31921jamie						    &((struct sockaddr_in *)
5553b31921jamie						    (void *)ai->ai_addr)->
5563b31921jamie						    sin_addr, sizeof(addr4));
5573b31921jamie						if (inet_ntop(AF_INET,
5583b31921jamie						    &addr4, avalue4,
5593b31921jamie						    INET_ADDRSTRLEN) == NULL)
5603b31921jamie							err(1, "inet_ntop");
561235aefejamie						add_param(j, NULL, KP_IP4_ADDR,
5623b31921jamie						    avalue4);
5633b31921jamie						break;
5646811668jamie#endif
5653b31921jamie#ifdef INET6
5663b31921jamie					case AF_INET6:
5673b31921jamie						memcpy(&addr6,
5683b31921jamie						    &((struct sockaddr_in6 *)
5693b31921jamie						    (void *)ai->ai_addr)->
5703b31921jamie						    sin6_addr, sizeof(addr6));
5713b31921jamie						if (inet_ntop(AF_INET6,
5723b31921jamie						    &addr6, avalue6,
5733b31921jamie						    INET6_ADDRSTRLEN) == NULL)
5743b31921jamie							err(1, "inet_ntop");
575235aefejamie						add_param(j, NULL, KP_IP6_ADDR,
5763b31921jamie						    avalue6);
5773b31921jamie						break;
5783b31921jamie#endif
5793b31921jamie					}
5803b31921jamie				freeaddrinfo(ai0);
5813b31921jamie			}
5823b31921jamie		}
5833b31921jamie	}
58494aa5f7jamie
5853b31921jamie	/*
5863b31921jamie	 * IP addresses may include an interface to set that address on,
5871aebfbbsmh	 * a netmask/suffix for that address and options for ifconfig.
5881aebfbbsmh	 * These are copied to an internal command parameter and then stripped
5891aebfbbsmh	 * so they won't be passed on to jailparam_set.
5903b31921jamie	 */
5913b31921jamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
5926811668jamie#ifdef INET
5936811668jamie	if (j->intparams[KP_IP4_ADDR] != NULL) {
5946811668jamie		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
5953b31921jamie			cs = strchr(s->s, '|');
596235aefejamie			if (cs || defif)
5976811668jamie				add_param(j, NULL, IP__IP4_IFADDR, s->s);
598235aefejamie			if (cs) {
599235aefejamie				strcpy(s->s, cs + 1);
600235aefejamie				s->len -= cs + 1 - s->s;
6013b31921jamie			}
6022595bebjamie			if ((cs = strchr(s->s, '/')) != NULL) {
60344f6adcjamie				*cs = '\0';
6042c0fa14jamie				s->len = cs - s->s;
6056811668jamie			}
6061aebfbbsmh			if ((cs = strchr(s->s, ' ')) != NULL) {
6071aebfbbsmh				*cs = '\0';
6081aebfbbsmh				s->len = cs - s->s;
6091aebfbbsmh			}
6106811668jamie		}
6116811668jamie	}
6120cc1eb5jamie#endif
6136811668jamie#ifdef INET6
6146811668jamie	if (j->intparams[KP_IP6_ADDR] != NULL) {
6156811668jamie		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
6166811668jamie			cs = strchr(s->s, '|');
6176811668jamie			if (cs || defif)
6186811668jamie				add_param(j, NULL, IP__IP6_IFADDR, s->s);
6196811668jamie			if (cs) {
6206811668jamie				strcpy(s->s, cs + 1);
6216811668jamie				s->len -= cs + 1 - s->s;
6226811668jamie			}
6232595bebjamie			if ((cs = strchr(s->s, '/')) != NULL) {
62444f6adcjamie				*cs = '\0';
6252c0fa14jamie				s->len = cs - s->s;
6263b31921jamie			}
6271aebfbbsmh			if ((cs = strchr(s->s, ' ')) != NULL) {
6281aebfbbsmh				*cs = '\0';
6291aebfbbsmh				s->len = cs - s->s;
6301aebfbbsmh			}
6313b31921jamie		}
6323b31921jamie	}
6336811668jamie#endif
6343b31921jamie#endif
6358576789jamie
6368576789jamie	/*
6378576789jamie	 * Read mount.fstab file(s), and treat each line as its own mount
6388576789jamie	 * parameter.
6398576789jamie	 */
6408576789jamie	if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
6410e5ec9djamie		TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
6428576789jamie			if (s->len == 0)
6438576789jamie				continue;
6448576789jamie			f = fopen(s->s, "r");
6458576789jamie			if (f == NULL) {
6468576789jamie				jail_warnx(j, "mount.fstab: %s: %s",
6478576789jamie				    s->s, strerror(errno));
6488576789jamie				error = -1;
6498576789jamie				continue;
6508576789jamie			}
6518576789jamie			while ((ln = fgetln(f, &lnlen))) {
6528576789jamie				if ((cs = memchr(ln, '#', lnlen - 1)))
6538576789jamie					lnlen = cs - ln + 1;
6548576789jamie				if (ln[lnlen - 1] == '\n' ||
6558576789jamie				    ln[lnlen - 1] == '#')
6568576789jamie					ln[lnlen - 1] = '\0';
6578576789jamie				else {
6588576789jamie					cs = alloca(lnlen + 1);
6598576789jamie					strlcpy(cs, ln, lnlen + 1);
6608576789jamie					ln = cs;
6618576789jamie				}
6628576789jamie				add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
6638576789jamie			}
6648576789jamie			fclose(f);
6658576789jamie		}
6668576789jamie	}
6678576789jamie	if (error)
6688576789jamie		failed(j);
6693b31921jamie	return error;
6703b31921jamie}
6713b31921jamie
6723b31921jamie/*
6733b31921jamie * Import parameters into libjail's binary jailparam format.
6743b31921jamie */
6753b31921jamieint
6763b31921jamieimport_params(struct cfjail *j)
6773b31921jamie{
6783b31921jamie	struct cfparam *p;
6793b31921jamie	struct cfstring *s, *ts;
6803b31921jamie	struct jailparam *jp;
6813b31921jamie	char *value, *cs;
6823b31921jamie	size_t vallen;
6833b31921jamie	int error;
6843b31921jamie
6853b31921jamie	error = 0;
6863b31921jamie	j->njp = 0;
6873b31921jamie	TAILQ_FOREACH(p, &j->params, tq)
6883b31921jamie		if (!(p->flags & PF_INTERNAL))
6893b31921jamie			j->njp++;
6903b31921jamie	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
6913b31921jamie	TAILQ_FOREACH(p, &j->params, tq) {
6923b31921jamie		if (p->flags & PF_INTERNAL)
6933b31921jamie			continue;
6943b31921jamie		if (jailparam_init(jp, p->name) < 0) {
6953b31921jamie			error = -1;
6963b31921jamie			jail_warnx(j, "%s", jail_errmsg);
6971b32102jamie			jp++;
6983b31921jamie			continue;
6993b31921jamie		}
7000e5ec9djamie		if (TAILQ_EMPTY(&p->val))
7013b31921jamie			value = NULL;
7023b31921jamie		else if (!jp->jp_elemlen ||
7030e5ec9djamie			 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
7043b31921jamie			/*
7053b31921jamie			 * Scalar parameters silently discard multiple (array)
7063b31921jamie			 * values, keeping only the last value added.  This
7073b31921jamie			 * lets values added from the command line append to
7083b31921jamie			 * arrays wthout pre-checking the type.
7093b31921jamie			 */
7100e5ec9djamie			value = TAILQ_LAST(&p->val, cfstrings)->s;
7113b31921jamie		} else {
7123b31921jamie			/*
7133b31921jamie			 * Convert arrays into comma-separated strings, which
7143b31921jamie			 * jailparam_import will then convert back into arrays.
7153b31921jamie			 */
7163b31921jamie			vallen = 0;
7170e5ec9djamie			TAILQ_FOREACH(s, &p->val, tq)
7183b31921jamie				vallen += s->len + 1;
7193b31921jamie			value = alloca(vallen);
7203b31921jamie			cs = value;
7210e5ec9djamie			TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
7222c0fa14jamie				memcpy(cs, s->s, s->len);
72344f6adcjamie				cs += s->len + 1;
72444f6adcjamie				cs[-1] = ',';
7253b31921jamie			}
72644f6adcjamie			value[vallen - 1] = '\0';
7273b31921jamie		}
7283b31921jamie		if (jailparam_import(jp, value) < 0) {
7293b31921jamie			error = -1;
7303b31921jamie			jail_warnx(j, "%s", jail_errmsg);
7313b31921jamie		}
7323b31921jamie		jp++;
7333b31921jamie	}
7343b31921jamie	if (error) {
7353b31921jamie		jailparam_free(j->jp, j->njp);
7363b31921jamie		free(j->jp);
7373b31921jamie		j->jp = NULL;
7383b31921jamie		failed(j);
7393b31921jamie	}
7403b31921jamie	return error;
7413b31921jamie}
7423b31921jamie
7433b31921jamie/*
7443b31921jamie * Check if options are equal (with or without the "no" prefix).
7453b31921jamie */
7463b31921jamieint
7473b31921jamieequalopts(const char *opt1, const char *opt2)
7483b31921jamie{
7493b31921jamie	char *p;
7503b31921jamie
7513b31921jamie	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
7523b31921jamie	if (strcmp(opt1, opt2) == 0)
7533b31921jamie		return (1);
7543b31921jamie	/* "noopt" vs. "opt" */
7553b31921jamie	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
7563b31921jamie		return (1);
7573b31921jamie	/* "opt" vs. "noopt" */
7583b31921jamie	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
7593b31921jamie		return (1);
7603b31921jamie	while ((p = strchr(opt1, '.')) != NULL &&
7613b31921jamie	    !strncmp(opt1, opt2, ++p - opt1)) {
7623b31921jamie		opt2 += p - opt1;
7633b31921jamie		opt1 = p;
7643b31921jamie		/* "foo.noopt" vs. "foo.opt" */
7653b31921jamie		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
7663b31921jamie			return (1);
7673b31921jamie		/* "foo.opt" vs. "foo.noopt" */
7683b31921jamie		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
7693b31921jamie			return (1);
7703b31921jamie	}
7713b31921jamie	return (0);
7723b31921jamie}
7733b31921jamie
7743b31921jamie/*
7753b31921jamie * See if a jail name matches a wildcard.
7763b31921jamie */
7773b31921jamieint
7783b31921jamiewild_jail_match(const char *jname, const char *wname)
7793b31921jamie{
7803b31921jamie	const char *jc, *jd, *wc, *wd;
7813b31921jamie
7823b31921jamie	/*
7833b31921jamie	 * A non-final "*" component in the wild name matches a single jail
7843b31921jamie	 * component, and a final "*" matches one or more jail components.
7853b31921jamie	 */
7863b31921jamie	for (jc = jname, wc = wname;
7873b31921jamie	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
7883b31921jamie	     jc = jd + 1, wc = wd + 1)
7893b31921jamie		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
7903b31921jamie			return 0;
7913b31921jamie	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
7923b31921jamie}
7933b31921jamie
7943b31921jamie/*
7953b31921jamie * Return if a jail name is a wildcard.
7963b31921jamie */
7973b31921jamieint
7983b31921jamiewild_jail_name(const char *wname)
7993b31921jamie{
8003b31921jamie	const char *wc;
8013b31921jamie
8023b31921jamie	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
8033b31921jamie		if ((wc == wname || wc[-1] == '.') &&
8043b31921jamie		    (wc[1] == '\0' || wc[1] == '.'))
8053b31921jamie			return 1;
8063b31921jamie	return 0;
8073b31921jamie}
8083b31921jamie
8093b31921jamie/*
8103b31921jamie * Free a parameter record and all its strings and variables.
8113b31921jamie */
8123b31921jamiestatic void
8133b31921jamiefree_param(struct cfparams *pp, struct cfparam *p)
8143b31921jamie{
8153b31921jamie	free(p->name);
8163b31921jamie	free_param_strings(p);
8173b31921jamie	TAILQ_REMOVE(pp, p, tq);
8183b31921jamie	free(p);
8193b31921jamie}
8203b31921jamie
8213b31921jamiestatic void
8223b31921jamiefree_param_strings(struct cfparam *p)
8233b31921jamie{
8243b31921jamie	struct cfstring *s;
8253b31921jamie	struct cfvar *v;
8263b31921jamie
8270e5ec9djamie	while ((s = TAILQ_FIRST(&p->val))) {
8283b31921jamie		free(s->s);
8293b31921jamie		while ((v = STAILQ_FIRST(&s->vars))) {
8303b31921jamie			free(v->name);
8313b31921jamie			STAILQ_REMOVE_HEAD(&s->vars, tq);
8323b31921jamie			free(v);
8333b31921jamie		}
8340e5ec9djamie		TAILQ_REMOVE(&p->val, s, tq);
8353b31921jamie		free(s);
8363b31921jamie	}
8373b31921jamie}
838