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},
749fcd25bfreqlabs    [IP_EXEC_PREPARE] =		{"exec.prepare",	PF_INTERNAL},
75235aefejamie    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
76235aefejamie    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
779fcd25bfreqlabs    [IP_EXEC_RELEASE] =		{"exec.release",	PF_INTERNAL},
7842ccecbnetchild    [IP_EXEC_CREATED] =		{"exec.created",	PF_INTERNAL},
79235aefejamie    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
80235aefejamie    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
81235aefejamie    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
82235aefejamie							PF_INTERNAL | PF_BOOL},
83235aefejamie    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
84235aefejamie    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
856811668jamie#if defined(INET) || defined(INET6)
86235aefejamie    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
87235aefejamie    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
886811668jamie#endif
8982d5811jamie    [IP_MOUNT] =		{"mount",		PF_INTERNAL | PF_REV},
90235aefejamie    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
91513bdd9hrs    [IP_MOUNT_FDESCFS] =	{"mount.fdescfs",	PF_INTERNAL | PF_BOOL},
926064614jamie    [IP_MOUNT_PROCFS] =		{"mount.procfs",	PF_INTERNAL | PF_BOOL},
93235aefejamie    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
94235aefejamie    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
95235aefejamie    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
966811668jamie#ifdef INET
9782d5811jamie    [IP__IP4_IFADDR] =		{"ip4.addr",	PF_INTERNAL | PF_CONV | PF_REV},
986811668jamie#endif
993b31921jamie#ifdef INET6
10082d5811jamie    [IP__IP6_IFADDR] =		{"ip6.addr",	PF_INTERNAL | PF_CONV | PF_REV},
1013b31921jamie#endif
10282d5811jamie    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",	PF_INTERNAL | PF_CONV | PF_REV},
103bf5da84jamie    [IP__OP] =			{NULL,			PF_CONV},
104235aefejamie    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
105235aefejamie    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
106235aefejamie    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
107235aefejamie    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
108235aefejamie    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
109235aefejamie    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
1106fe59c6jamie    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
111235aefejamie    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
1126811668jamie#ifdef INET
113235aefejamie    [KP_IP4_ADDR] =		{"ip4.addr",		0},
1146811668jamie#endif
115235aefejamie#ifdef INET6
116235aefejamie    [KP_IP6_ADDR] =		{"ip6.addr",		0},
117235aefejamie#endif
1186a6f426hrs    [KP_JID] =			{"jid",			PF_IMMUTABLE},
1196a6f426hrs    [KP_NAME] =			{"name",		PF_IMMUTABLE},
120235aefejamie    [KP_PATH] =			{"path",		0},
121235aefejamie    [KP_PERSIST] =		{"persist",		0},
122235aefejamie    [KP_SECURELEVEL] =		{"securelevel",		0},
123235aefejamie    [KP_VNET] =			{"vnet",		0},
1243b31921jamie};
1253b31921jamie
1263b31921jamie/*
1273b31921jamie * Parse the jail configuration file.
1283b31921jamie */
1293b31921jamievoid
1303b31921jamieload_config(void)
1313b31921jamie{
1323b31921jamie	struct cfjails wild;
1333b31921jamie	struct cfparams opp;
1343b31921jamie	struct cfjail *j, *tj, *wj;
1353b31921jamie	struct cfparam *p, *vp, *tp;
1363b31921jamie	struct cfstring *s, *vs, *ns;
1373c6c216hrs	struct cfvar *v, *vv;
1383b31921jamie	char *ep;
1393b31921jamie	int did_self, jseq, pgen;
1403b31921jamie
1413b31921jamie	if (!strcmp(cfname, "-")) {
1423b31921jamie		cfname = "STDIN";
1433b31921jamie		yyin = stdin;
1443b31921jamie	} else {
1453b31921jamie		yyin = fopen(cfname, "r");
1463b31921jamie		if (!yyin)
1473b31921jamie			err(1, "%s", cfname);
1483b31921jamie	}
1493b31921jamie	if (yyparse() || yynerrs)
1503b31921jamie		exit(1);
1513b31921jamie
1523b31921jamie	/* Separate the wildcard jails out from the actual jails. */
1533b31921jamie	jseq = 0;
1543b31921jamie	TAILQ_INIT(&wild);
1553b31921jamie	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
1563b31921jamie		j->seq = ++jseq;
1573b31921jamie		if (wild_jail_name(j->name))
1583b31921jamie			requeue(j, &wild);
1593b31921jamie	}
1603b31921jamie
1613b31921jamie	TAILQ_FOREACH(j, &cfjails, tq) {
1623b31921jamie		/* Set aside the jail's parameters. */
1633b31921jamie		TAILQ_INIT(&opp);
1643b31921jamie		TAILQ_CONCAT(&opp, &j->params, tq);
1653b31921jamie		/*
1663b31921jamie		 * The jail name implies its "name" or "jid" parameter,
1673b31921jamie		 * though they may also be explicitly set later on.
1683b31921jamie		 */
1693b31921jamie		add_param(j, NULL,
170235aefejamie		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
1713b31921jamie		    j->name);
1723b31921jamie		/*
1733b31921jamie		 * Collect parameters for the jail, global parameters/variables,
1743b31921jamie		 * and any matching wildcard jails.
1753b31921jamie		 */
1763b31921jamie		did_self = 0;
1773b31921jamie		TAILQ_FOREACH(wj, &wild, tq) {
1783b31921jamie			if (j->seq < wj->seq && !did_self) {
1793b31921jamie				TAILQ_FOREACH(p, &opp, tq)
180235aefejamie					add_param(j, p, 0, NULL);
1813b31921jamie				did_self = 1;
1823b31921jamie			}
1833b31921jamie			if (wild_jail_match(j->name, wj->name))
1843b31921jamie				TAILQ_FOREACH(p, &wj->params, tq)
185235aefejamie					add_param(j, p, 0, NULL);
1863b31921jamie		}
1873b31921jamie		if (!did_self)
1883b31921jamie			TAILQ_FOREACH(p, &opp, tq)
189235aefejamie				add_param(j, p, 0, NULL);
1903b31921jamie
1913b31921jamie		/* Resolve any variable substitutions. */
1923b31921jamie		pgen = 0;
1933b31921jamie		TAILQ_FOREACH(p, &j->params, tq) {
1943b31921jamie		    p->gen = ++pgen;
1953b31921jamie		find_vars:
1960e5ec9djamie		    TAILQ_FOREACH(s, &p->val, tq) {
1973b31921jamie			while ((v = STAILQ_FIRST(&s->vars))) {
1983b31921jamie				TAILQ_FOREACH(vp, &j->params, tq)
1993b31921jamie					if (!strcmp(vp->name, v->name))
2003b31921jamie						break;
2013b31921jamie				if (!vp) {
2023b31921jamie					jail_warnx(j,
2033b31921jamie					    "%s: variable \"%s\" not found",
2043b31921jamie					    p->name, v->name);
2053b31921jamie				bad_var:
2063b31921jamie					j->flags |= JF_FAILED;
2073b31921jamie					TAILQ_FOREACH(vp, &j->params, tq)
2083b31921jamie						if (vp->gen == pgen)
2093b31921jamie							vp->flags |= PF_BAD;
2103b31921jamie					goto free_var;
2113b31921jamie				}
2123b31921jamie				if (vp->flags & PF_BAD)
2133b31921jamie					goto bad_var;
2143b31921jamie				if (vp->gen == pgen) {
2153b31921jamie					jail_warnx(j, "%s: variable loop",
2163b31921jamie					    v->name);
2173b31921jamie					goto bad_var;
2183b31921jamie				}
2190e5ec9djamie				TAILQ_FOREACH(vs, &vp->val, tq)
2203b31921jamie					if (!STAILQ_EMPTY(&vs->vars)) {
2213b31921jamie						vp->gen = pgen;
2223b31921jamie						TAILQ_REMOVE(&j->params, vp,
2233b31921jamie						    tq);
2243b31921jamie						TAILQ_INSERT_BEFORE(p, vp, tq);
2253b31921jamie						p = vp;
2263b31921jamie						goto find_vars;
2273b31921jamie					}
2280e5ec9djamie				vs = TAILQ_FIRST(&vp->val);
2290e5ec9djamie				if (TAILQ_NEXT(vs, tq) != NULL &&
2303b31921jamie				    (s->s[0] != '\0' ||
2313b31921jamie				     STAILQ_NEXT(v, tq))) {
2323b31921jamie					jail_warnx(j, "%s: array cannot be "
2333b31921jamie					    "substituted inline",
2343b31921jamie					    p->name);
2353b31921jamie					goto bad_var;
2363b31921jamie				}
2373b31921jamie				s->s = erealloc(s->s, s->len + vs->len + 1);
2383c6c216hrs				memmove(s->s + v->pos + vs->len,
2393c6c216hrs				    s->s + v->pos,
2403c6c216hrs				    s->len - v->pos + 1);
2413c6c216hrs				memcpy(s->s + v->pos, vs->s, vs->len);
2423c6c216hrs				vv = v;
2433c6c216hrs				while ((vv = STAILQ_NEXT(vv, tq)))
2443c6c216hrs					vv->pos += vs->len;
2453b31921jamie				s->len += vs->len;
2460e5ec9djamie				while ((vs = TAILQ_NEXT(vs, tq))) {
2473b31921jamie					ns = emalloc(sizeof(struct cfstring));
2483b31921jamie					ns->s = estrdup(vs->s);
2493b31921jamie					ns->len = vs->len;
2503b31921jamie					STAILQ_INIT(&ns->vars);
2510e5ec9djamie					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
2523b31921jamie					s = ns;
2533b31921jamie				}
2543b31921jamie			free_var:
2553b31921jamie				free(v->name);
2563b31921jamie				STAILQ_REMOVE_HEAD(&s->vars, tq);
2573b31921jamie				free(v);
2583b31921jamie			}
2593b31921jamie		    }
2603b31921jamie		}
2613b31921jamie
2623b31921jamie		/* Free the jail's original parameter list and any variables. */
2633b31921jamie		while ((p = TAILQ_FIRST(&opp)))
2643b31921jamie			free_param(&opp, p);
2653b31921jamie		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
2663b31921jamie			if (p->flags & PF_VAR)
2673b31921jamie				free_param(&j->params, p);
2683b31921jamie	}
2693b31921jamie	while ((wj = TAILQ_FIRST(&wild))) {
2703b31921jamie		free(wj->name);
2713b31921jamie		while ((p = TAILQ_FIRST(&wj->params)))
2723b31921jamie			free_param(&wj->params, p);
2733b31921jamie		TAILQ_REMOVE(&wild, wj, tq);
2743b31921jamie	}
2753b31921jamie}
2763b31921jamie
2773b31921jamie/*
2783b31921jamie * Create a new jail record.
2793b31921jamie */
2803b31921jamiestruct cfjail *
2813b31921jamieadd_jail(void)
2823b31921jamie{
2833b31921jamie	struct cfjail *j;
2843b31921jamie
2853b31921jamie	j = emalloc(sizeof(struct cfjail));
2863b31921jamie	memset(j, 0, sizeof(struct cfjail));
2873b31921jamie	TAILQ_INIT(&j->params);
2883b31921jamie	STAILQ_INIT(&j->dep[DEP_FROM]);
2893b31921jamie	STAILQ_INIT(&j->dep[DEP_TO]);
2903b31921jamie	j->queue = &cfjails;
2913b31921jamie	TAILQ_INSERT_TAIL(&cfjails, j, tq);
2923b31921jamie	return j;
2933b31921jamie}
2943b31921jamie
2953b31921jamie/*
2963b31921jamie * Add a parameter to a jail.
2973b31921jamie */
2983b31921jamievoid
299235aefejamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
3003b31921jamie    const char *value)
3013b31921jamie{
3023b31921jamie	struct cfstrings nss;
3033b31921jamie	struct cfparam *dp, *np;
3043b31921jamie	struct cfstring *s, *ns;
3053b31921jamie	struct cfvar *v, *nv;
306235aefejamie	const char *name;
307235aefejamie	char *cs, *tname;
3083b31921jamie	unsigned flags;
3093b31921jamie
3103b31921jamie	if (j == NULL) {
3113b31921jamie		/* Create a single anonymous jail if one doesn't yet exist. */
3123b31921jamie		j = TAILQ_LAST(&cfjails, cfjails);
3133b31921jamie		if (j == NULL)
3143b31921jamie			j = add_jail();
3153b31921jamie	}
3160e5ec9djamie	TAILQ_INIT(&nss);
3173b31921jamie	if (p != NULL) {
3183b31921jamie		name = p->name;
3193b31921jamie		flags = p->flags;
3203b31921jamie		/*
3213b31921jamie		 * Make a copy of the parameter's string list,
3223b31921jamie		 * which may be freed if it's overridden later.
3233b31921jamie		 */
3240e5ec9djamie		TAILQ_FOREACH(s, &p->val, tq) {
3253b31921jamie			ns = emalloc(sizeof(struct cfstring));
3263b31921jamie			ns->s = estrdup(s->s);
3273b31921jamie			ns->len = s->len;
3283b31921jamie			STAILQ_INIT(&ns->vars);
3293b31921jamie			STAILQ_FOREACH(v, &s->vars, tq) {
3303b31921jamie				nv = emalloc(sizeof(struct cfvar));
3313b31921jamie				nv->name = strdup(v->name);
3323b31921jamie				nv->pos = v->pos;
3333b31921jamie				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
3343b31921jamie			}
3350e5ec9djamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
3363b31921jamie		}
3373b31921jamie	} else {
3383b31921jamie		flags = PF_APPEND;
33918b00cejamie		if (ipnum != IP__NULL) {
340235aefejamie			name = intparams[ipnum].name;
341235aefejamie			flags |= intparams[ipnum].flags;
342235aefejamie		} else if ((cs = strchr(value, '='))) {
343235aefejamie			tname = alloca(cs - value + 1);
344235aefejamie			strlcpy(tname, value, cs - value + 1);
345235aefejamie			name = tname;
346235aefejamie			value = cs + 1;
347235aefejamie		} else {
348235aefejamie			name = value;
349235aefejamie			value = NULL;
350235aefejamie		}
3513b31921jamie		if (value != NULL) {
3523b31921jamie			ns = emalloc(sizeof(struct cfstring));
3533b31921jamie			ns->s = estrdup(value);
3543b31921jamie			ns->len = strlen(value);
3553b31921jamie			STAILQ_INIT(&ns->vars);
3560e5ec9djamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
3573b31921jamie		}
3583b31921jamie	}
3593b31921jamie
3603b31921jamie	/* See if this parameter has already been added. */
36118b00cejamie	if (ipnum != IP__NULL)
362235aefejamie		dp = j->intparams[ipnum];
363235aefejamie	else
364235aefejamie		TAILQ_FOREACH(dp, &j->params, tq)
365235aefejamie			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
366235aefejamie				break;
367235aefejamie	if (dp != NULL) {
368235aefejamie		/* Found it - append or replace. */
36911ad939jamie		if ((flags ^ dp->flags) & PF_VAR) {
37011ad939jamie			jail_warnx(j, "variable \"$%s\" cannot have the same "
37111ad939jamie			    "name as a parameter.", name);
372f5ab5a0jamie			j->flags |= JF_FAILED;
37311ad939jamie			return;
37411ad939jamie		}
3756a6f426hrs		if (dp->flags & PF_IMMUTABLE) {
37611ad939jamie			jail_warnx(j, "cannot redefine parameter \"%s\".",
3776a6f426hrs			    dp->name);
378f5ab5a0jamie			j->flags |= JF_FAILED;
3796a6f426hrs			return;
3806a6f426hrs		}
381235aefejamie		if (strcmp(dp->name, name)) {
382235aefejamie			free(dp->name);
383235aefejamie			dp->name = estrdup(name);
3843b31921jamie		}
3850e5ec9djamie		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
386235aefejamie			free_param_strings(dp);
3870e5ec9djamie		TAILQ_CONCAT(&dp->val, &nss, tq);
388235aefejamie		dp->flags |= flags;
389235aefejamie	} else {
3903b31921jamie		/* Not found - add it. */
3913b31921jamie		np = emalloc(sizeof(struct cfparam));
3923b31921jamie		np->name = estrdup(name);
3930e5ec9djamie		TAILQ_INIT(&np->val);
3940e5ec9djamie		TAILQ_CONCAT(&np->val, &nss, tq);
3953b31921jamie		np->flags = flags;
3963b31921jamie		np->gen = 0;
3973b31921jamie		TAILQ_INSERT_TAIL(&j->params, np, tq);
39818b00cejamie		if (ipnum != IP__NULL)
399235aefejamie			j->intparams[ipnum] = np;
400235aefejamie		else
40118b00cejamie			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
402235aefejamie				if (!(intparams[ipnum].flags & PF_CONV) &&
403e8619bdjamie				    equalopts(name, intparams[ipnum].name)) {
40411ad939jamie					if (flags & PF_VAR) {
40511ad939jamie						jail_warnx(j,
40611ad939jamie						    "variable \"$%s\" "
40711ad939jamie						    "cannot have the same "
40811ad939jamie						    "name as a parameter.",
40911ad939jamie						    name);
410f5ab5a0jamie						j->flags |= JF_FAILED;
41111ad939jamie						return;
41211ad939jamie					}
413235aefejamie					j->intparams[ipnum] = np;
414235aefejamie					np->flags |= intparams[ipnum].flags;
415235aefejamie					break;
416235aefejamie				}
4173b31921jamie	}
4183b31921jamie}
4193b31921jamie
4203b31921jamie/*
4213b31921jamie * Return if a boolean parameter exists and is true.
4223b31921jamie */
4233b31921jamieint
4243b31921jamiebool_param(const struct cfparam *p)
4253b31921jamie{
4263b31921jamie	const char *cs;
4273b31921jamie
4283b31921jamie	if (p == NULL)
4293b31921jamie		return 0;
4303b31921jamie	cs = strrchr(p->name, '.');
4313b31921jamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
4320e5ec9djamie	    (TAILQ_EMPTY(&p->val) ||
4330e5ec9djamie	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
4340e5ec9djamie	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
4353b31921jamie}
4363b31921jamie
4373b31921jamie/*
4383b31921jamie * Set an integer if a parameter if it exists.
4393b31921jamie */
4403b31921jamieint
4413b31921jamieint_param(const struct cfparam *p, int *ip)
4423b31921jamie{
4430e5ec9djamie	if (p == NULL || TAILQ_EMPTY(&p->val))
4443b31921jamie		return 0;
4450e5ec9djamie	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
4463b31921jamie	return 1;
4473b31921jamie}
4483b31921jamie
4493b31921jamie/*
4503b31921jamie * Return the string value of a scalar parameter if it exists.
4513b31921jamie */
4523b31921jamieconst char *
4533b31921jamiestring_param(const struct cfparam *p)
4543b31921jamie{
4550e5ec9djamie	return (p && !TAILQ_EMPTY(&p->val)
4560e5ec9djamie	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
4573b31921jamie}
4583b31921jamie
4593b31921jamie/*
46094aa5f7jamie * Check syntax and values of internal parameters.  Set some internal
46194aa5f7jamie * parameters based on the values of others.
4623b31921jamie */
4633b31921jamieint
46494aa5f7jamiecheck_intparams(struct cfjail *j)
4653b31921jamie{
46694aa5f7jamie	struct cfparam *p;
4676a72e94jamie	struct cfstring *s;
4688576789jamie	FILE *f;
4696811668jamie	const char *val;
4708576789jamie	char *cs, *ep, *ln;
4716811668jamie	size_t lnlen;
4726811668jamie	int error;
4736811668jamie#if defined(INET) || defined(INET6)
4746811668jamie	struct addrinfo hints;
4756811668jamie	struct addrinfo *ai0, *ai;
4766811668jamie	const char *hostname;
4772595bebjamie	int gicode, defif;
4786811668jamie#endif
4796811668jamie#ifdef INET
4806811668jamie	struct in_addr addr4;
4816811668jamie	int ip4ok;
4823b31921jamie	char avalue4[INET_ADDRSTRLEN];
4836811668jamie#endif
4843b31921jamie#ifdef INET6
4853b31921jamie	struct in6_addr addr6;
4866811668jamie	int ip6ok;
4873b31921jamie	char avalue6[INET6_ADDRSTRLEN];
4883b31921jamie#endif
4893b31921jamie
4903b31921jamie	error = 0;
49194aa5f7jamie	/* Check format of boolan and integer values. */
49294aa5f7jamie	TAILQ_FOREACH(p, &j->params, tq) {
4930e5ec9djamie		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
4940e5ec9djamie			val = TAILQ_LAST(&p->val, cfstrings)->s;
49594aa5f7jamie			if (p->flags & PF_BOOL) {
49694aa5f7jamie				if (strcasecmp(val, "false") &&
49794aa5f7jamie				    strcasecmp(val, "true") &&
49894aa5f7jamie				    ((void)strtol(val, &ep, 10), *ep)) {
49994aa5f7jamie					jail_warnx(j,
50094aa5f7jamie					    "%s: unknown boolean value \"%s\"",
50194aa5f7jamie					    p->name, val);
50294aa5f7jamie					error = -1;
50394aa5f7jamie				}
50494aa5f7jamie			} else {
50594aa5f7jamie				(void)strtol(val, &ep, 10);
50694aa5f7jamie				if (ep == val || *ep) {
50794aa5f7jamie					jail_warnx(j,
50894aa5f7jamie					    "%s: non-integer value \"%s\"",
50994aa5f7jamie					    p->name, val);
51094aa5f7jamie					error = -1;
51194aa5f7jamie				}
51294aa5f7jamie			}
51394aa5f7jamie		}
51494aa5f7jamie	}
51594aa5f7jamie
5166811668jamie#if defined(INET) || defined(INET6)
5173b31921jamie	/*
5183b31921jamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
5193b31921jamie	 * for any IP addresses it finds.
5203b31921jamie	 */
52194aa5f7jamie	if (((j->flags & JF_OP_MASK) != JF_STOP ||
52294aa5f7jamie	    j->intparams[IP_INTERFACE] != NULL) &&
52394aa5f7jamie	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
524235aefejamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
5253b31921jamie		j->intparams[IP_IP_HOSTNAME] = NULL;
5263b31921jamie		/*
5273b31921jamie		 * Silently ignore unsupported address families from
5283b31921jamie		 * DNS lookups.
5293b31921jamie		 */
5306811668jamie#ifdef INET
5316811668jamie		ip4ok = feature_present("inet");
5323b31921jamie#endif
5333b31921jamie#ifdef INET6
5346811668jamie		ip6ok = feature_present("inet6");
5356811668jamie#endif
5366811668jamie		if (
5376811668jamie#if defined(INET) && defined(INET6)
5386811668jamie		    ip4ok || ip6ok
5396811668jamie#elif defined(INET)
5406811668jamie		    ip4ok
5416811668jamie#elif defined(INET6)
5426811668jamie		    ip6ok
5433b31921jamie#endif
5446811668jamie			 ) {
5453b31921jamie			/* Look up the hostname (or get the address) */
5463b31921jamie			memset(&hints, 0, sizeof(hints));
5473b31921jamie			hints.ai_socktype = SOCK_STREAM;
5483b31921jamie			hints.ai_family =
5496811668jamie#if defined(INET) && defined(INET6)
5506811668jamie			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
5516811668jamie#elif defined(INET)
5523b31921jamie			    PF_INET;
5536811668jamie#elif defined(INET6)
5546811668jamie			    PF_INET6;
5556811668jamie#endif
55694aa5f7jamie			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
55794aa5f7jamie			if (gicode != 0) {
5583b31921jamie				jail_warnx(j, "host.hostname %s: %s", hostname,
55994aa5f7jamie				    gai_strerror(gicode));
5603b31921jamie				error = -1;
5613b31921jamie			} else {
5623b31921jamie				/*
5633b31921jamie				 * Convert the addresses to ASCII so jailparam
5643b31921jamie				 * can convert them back.  Errors are not
5653b31921jamie				 * expected here.
5663b31921jamie				 */
5673b31921jamie				for (ai = ai0; ai; ai = ai->ai_next)
5683b31921jamie					switch (ai->ai_family) {
5696811668jamie#ifdef INET
5703b31921jamie					case AF_INET:
5713b31921jamie						memcpy(&addr4,
5723b31921jamie						    &((struct sockaddr_in *)
5733b31921jamie						    (void *)ai->ai_addr)->
5743b31921jamie						    sin_addr, sizeof(addr4));
5753b31921jamie						if (inet_ntop(AF_INET,
5763b31921jamie						    &addr4, avalue4,
5773b31921jamie						    INET_ADDRSTRLEN) == NULL)
5783b31921jamie							err(1, "inet_ntop");
579235aefejamie						add_param(j, NULL, KP_IP4_ADDR,
5803b31921jamie						    avalue4);
5813b31921jamie						break;
5826811668jamie#endif
5833b31921jamie#ifdef INET6
5843b31921jamie					case AF_INET6:
5853b31921jamie						memcpy(&addr6,
5863b31921jamie						    &((struct sockaddr_in6 *)
5873b31921jamie						    (void *)ai->ai_addr)->
5883b31921jamie						    sin6_addr, sizeof(addr6));
5893b31921jamie						if (inet_ntop(AF_INET6,
5903b31921jamie						    &addr6, avalue6,
5913b31921jamie						    INET6_ADDRSTRLEN) == NULL)
5923b31921jamie							err(1, "inet_ntop");
593235aefejamie						add_param(j, NULL, KP_IP6_ADDR,
5943b31921jamie						    avalue6);
5953b31921jamie						break;
5963b31921jamie#endif
5973b31921jamie					}
5983b31921jamie				freeaddrinfo(ai0);
5993b31921jamie			}
6003b31921jamie		}
6013b31921jamie	}
60294aa5f7jamie
6033b31921jamie	/*
6043b31921jamie	 * IP addresses may include an interface to set that address on,
6051aebfbbsmh	 * a netmask/suffix for that address and options for ifconfig.
6061aebfbbsmh	 * These are copied to an internal command parameter and then stripped
6071aebfbbsmh	 * so they won't be passed on to jailparam_set.
6083b31921jamie	 */
6093b31921jamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
6106811668jamie#ifdef INET
6116811668jamie	if (j->intparams[KP_IP4_ADDR] != NULL) {
6126811668jamie		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
6133b31921jamie			cs = strchr(s->s, '|');
614235aefejamie			if (cs || defif)
6156811668jamie				add_param(j, NULL, IP__IP4_IFADDR, s->s);
616235aefejamie			if (cs) {
617235aefejamie				s->len -= cs + 1 - s->s;
61847c5dbaandrew				memmove(s->s, cs + 1, s->len + 1);
6193b31921jamie			}
6202595bebjamie			if ((cs = strchr(s->s, '/')) != NULL) {
62144f6adcjamie				*cs = '\0';
6222c0fa14jamie				s->len = cs - s->s;
6236811668jamie			}
6241aebfbbsmh			if ((cs = strchr(s->s, ' ')) != NULL) {
6251aebfbbsmh				*cs = '\0';
6261aebfbbsmh				s->len = cs - s->s;
6271aebfbbsmh			}
6286811668jamie		}
6296811668jamie	}
6300cc1eb5jamie#endif
6316811668jamie#ifdef INET6
6326811668jamie	if (j->intparams[KP_IP6_ADDR] != NULL) {
6336811668jamie		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
6346811668jamie			cs = strchr(s->s, '|');
6356811668jamie			if (cs || defif)
6366811668jamie				add_param(j, NULL, IP__IP6_IFADDR, s->s);
6376811668jamie			if (cs) {
6386811668jamie				s->len -= cs + 1 - s->s;
63947c5dbaandrew				memmove(s->s, cs + 1, s->len + 1);
6406811668jamie			}
6412595bebjamie			if ((cs = strchr(s->s, '/')) != NULL) {
64244f6adcjamie				*cs = '\0';
6432c0fa14jamie				s->len = cs - s->s;
6443b31921jamie			}
6451aebfbbsmh			if ((cs = strchr(s->s, ' ')) != NULL) {
6461aebfbbsmh				*cs = '\0';
6471aebfbbsmh				s->len = cs - s->s;
6481aebfbbsmh			}
6493b31921jamie		}
6503b31921jamie	}
6516811668jamie#endif
6523b31921jamie#endif
6538576789jamie
6548576789jamie	/*
6558576789jamie	 * Read mount.fstab file(s), and treat each line as its own mount
6568576789jamie	 * parameter.
6578576789jamie	 */
6588576789jamie	if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
6590e5ec9djamie		TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
6608576789jamie			if (s->len == 0)
6618576789jamie				continue;
6628576789jamie			f = fopen(s->s, "r");
6638576789jamie			if (f == NULL) {
6648576789jamie				jail_warnx(j, "mount.fstab: %s: %s",
6658576789jamie				    s->s, strerror(errno));
6668576789jamie				error = -1;
6678576789jamie				continue;
6688576789jamie			}
6698576789jamie			while ((ln = fgetln(f, &lnlen))) {
6708576789jamie				if ((cs = memchr(ln, '#', lnlen - 1)))
6718576789jamie					lnlen = cs - ln + 1;
6728576789jamie				if (ln[lnlen - 1] == '\n' ||
6738576789jamie				    ln[lnlen - 1] == '#')
6748576789jamie					ln[lnlen - 1] = '\0';
6758576789jamie				else {
6768576789jamie					cs = alloca(lnlen + 1);
6778576789jamie					strlcpy(cs, ln, lnlen + 1);
6788576789jamie					ln = cs;
6798576789jamie				}
6808576789jamie				add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
6818576789jamie			}
6828576789jamie			fclose(f);
6838576789jamie		}
6848576789jamie	}
6858576789jamie	if (error)
6868576789jamie		failed(j);
6873b31921jamie	return error;
6883b31921jamie}
6893b31921jamie
6903b31921jamie/*
6913b31921jamie * Import parameters into libjail's binary jailparam format.
6923b31921jamie */
6933b31921jamieint
6943b31921jamieimport_params(struct cfjail *j)
6953b31921jamie{
6963b31921jamie	struct cfparam *p;
6973b31921jamie	struct cfstring *s, *ts;
6983b31921jamie	struct jailparam *jp;
6993b31921jamie	char *value, *cs;
7003b31921jamie	size_t vallen;
7013b31921jamie	int error;
7023b31921jamie
7033b31921jamie	error = 0;
7043b31921jamie	j->njp = 0;
7053b31921jamie	TAILQ_FOREACH(p, &j->params, tq)
7063b31921jamie		if (!(p->flags & PF_INTERNAL))
7073b31921jamie			j->njp++;
7083b31921jamie	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
7093b31921jamie	TAILQ_FOREACH(p, &j->params, tq) {
7103b31921jamie		if (p->flags & PF_INTERNAL)
7113b31921jamie			continue;
7123b31921jamie		if (jailparam_init(jp, p->name) < 0) {
7133b31921jamie			error = -1;
7143b31921jamie			jail_warnx(j, "%s", jail_errmsg);
7151b32102jamie			jp++;
7163b31921jamie			continue;
7173b31921jamie		}
7180e5ec9djamie		if (TAILQ_EMPTY(&p->val))
7193b31921jamie			value = NULL;
7203b31921jamie		else if (!jp->jp_elemlen ||
7210e5ec9djamie			 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
7223b31921jamie			/*
7233b31921jamie			 * Scalar parameters silently discard multiple (array)
7243b31921jamie			 * values, keeping only the last value added.  This
7253b31921jamie			 * lets values added from the command line append to
7263b31921jamie			 * arrays wthout pre-checking the type.
7273b31921jamie			 */
7280e5ec9djamie			value = TAILQ_LAST(&p->val, cfstrings)->s;
7293b31921jamie		} else {
7303b31921jamie			/*
7313b31921jamie			 * Convert arrays into comma-separated strings, which
7323b31921jamie			 * jailparam_import will then convert back into arrays.
7333b31921jamie			 */
7343b31921jamie			vallen = 0;
7350e5ec9djamie			TAILQ_FOREACH(s, &p->val, tq)
7363b31921jamie				vallen += s->len + 1;
7373b31921jamie			value = alloca(vallen);
7383b31921jamie			cs = value;
7390e5ec9djamie			TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
7402c0fa14jamie				memcpy(cs, s->s, s->len);
74144f6adcjamie				cs += s->len + 1;
74244f6adcjamie				cs[-1] = ',';
7433b31921jamie			}
74444f6adcjamie			value[vallen - 1] = '\0';
7453b31921jamie		}
7463b31921jamie		if (jailparam_import(jp, value) < 0) {
7473b31921jamie			error = -1;
7483b31921jamie			jail_warnx(j, "%s", jail_errmsg);
7493b31921jamie		}
7503b31921jamie		jp++;
7513b31921jamie	}
7523b31921jamie	if (error) {
7533b31921jamie		jailparam_free(j->jp, j->njp);
7543b31921jamie		free(j->jp);
7553b31921jamie		j->jp = NULL;
7563b31921jamie		failed(j);
7573b31921jamie	}
7583b31921jamie	return error;
7593b31921jamie}
7603b31921jamie
7613b31921jamie/*
7623b31921jamie * Check if options are equal (with or without the "no" prefix).
7633b31921jamie */
7643b31921jamieint
7653b31921jamieequalopts(const char *opt1, const char *opt2)
7663b31921jamie{
7673b31921jamie	char *p;
7683b31921jamie
7693b31921jamie	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
7703b31921jamie	if (strcmp(opt1, opt2) == 0)
7713b31921jamie		return (1);
7723b31921jamie	/* "noopt" vs. "opt" */
7733b31921jamie	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
7743b31921jamie		return (1);
7753b31921jamie	/* "opt" vs. "noopt" */
7763b31921jamie	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
7773b31921jamie		return (1);
7783b31921jamie	while ((p = strchr(opt1, '.')) != NULL &&
7793b31921jamie	    !strncmp(opt1, opt2, ++p - opt1)) {
7803b31921jamie		opt2 += p - opt1;
7813b31921jamie		opt1 = p;
7823b31921jamie		/* "foo.noopt" vs. "foo.opt" */
7833b31921jamie		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
7843b31921jamie			return (1);
7853b31921jamie		/* "foo.opt" vs. "foo.noopt" */
7863b31921jamie		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
7873b31921jamie			return (1);
7883b31921jamie	}
7893b31921jamie	return (0);
7903b31921jamie}
7913b31921jamie
7923b31921jamie/*
7933b31921jamie * See if a jail name matches a wildcard.
7943b31921jamie */
7953b31921jamieint
7963b31921jamiewild_jail_match(const char *jname, const char *wname)
7973b31921jamie{
7983b31921jamie	const char *jc, *jd, *wc, *wd;
7993b31921jamie
8003b31921jamie	/*
8013b31921jamie	 * A non-final "*" component in the wild name matches a single jail
8023b31921jamie	 * component, and a final "*" matches one or more jail components.
8033b31921jamie	 */
8043b31921jamie	for (jc = jname, wc = wname;
8053b31921jamie	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
8063b31921jamie	     jc = jd + 1, wc = wd + 1)
8073b31921jamie		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
8083b31921jamie			return 0;
8093b31921jamie	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
8103b31921jamie}
8113b31921jamie
8123b31921jamie/*
8133b31921jamie * Return if a jail name is a wildcard.
8143b31921jamie */
8153b31921jamieint
8163b31921jamiewild_jail_name(const char *wname)
8173b31921jamie{
8183b31921jamie	const char *wc;
8193b31921jamie
8203b31921jamie	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
8213b31921jamie		if ((wc == wname || wc[-1] == '.') &&
8223b31921jamie		    (wc[1] == '\0' || wc[1] == '.'))
8233b31921jamie			return 1;
8243b31921jamie	return 0;
8253b31921jamie}
8263b31921jamie
8273b31921jamie/*
8283b31921jamie * Free a parameter record and all its strings and variables.
8293b31921jamie */
8303b31921jamiestatic void
8313b31921jamiefree_param(struct cfparams *pp, struct cfparam *p)
8323b31921jamie{
8333b31921jamie	free(p->name);
8343b31921jamie	free_param_strings(p);
8353b31921jamie	TAILQ_REMOVE(pp, p, tq);
8363b31921jamie	free(p);
8373b31921jamie}
8383b31921jamie
8393b31921jamiestatic void
8403b31921jamiefree_param_strings(struct cfparam *p)
8413b31921jamie{
8423b31921jamie	struct cfstring *s;
8433b31921jamie	struct cfvar *v;
8443b31921jamie
8450e5ec9djamie	while ((s = TAILQ_FIRST(&p->val))) {
8463b31921jamie		free(s->s);
8473b31921jamie		while ((v = STAILQ_FIRST(&s->vars))) {
8483b31921jamie			free(v->name);
8493b31921jamie			STAILQ_REMOVE_HEAD(&s->vars, tq);
8503b31921jamie			free(v);
8513b31921jamie		}
8520e5ec9djamie		TAILQ_REMOVE(&p->val, s, tq);
8533b31921jamie		free(s);
8543b31921jamie	}
8553b31921jamie}
856