config.c revision 269805
138061Smsmith/*-
255939Snsouch * Copyright (c) 2011 James Gritton
370608Snsouch * All rights reserved.
438061Smsmith *
538061Smsmith * Redistribution and use in source and binary forms, with or without
638061Smsmith * modification, are permitted provided that the following conditions
738061Smsmith * are met:
838061Smsmith * 1. Redistributions of source code must retain the above copyright
938061Smsmith *    notice, this list of conditions and the following disclaimer.
1038061Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138061Smsmith *    notice, this list of conditions and the following disclaimer in the
1238061Smsmith *    documentation and/or other materials provided with the distribution.
1338061Smsmith *
1438061Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538061Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638061Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738061Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838061Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938061Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038061Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138061Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238061Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338061Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438061Smsmith * SUCH DAMAGE.
2538061Smsmith */
2638061Smsmith
2738061Smsmith#include <sys/cdefs.h>
2838061Smsmith__FBSDID("$FreeBSD: stable/10/usr.sbin/jail/config.c 269805 2014-08-11 08:58:35Z smh $");
2938061Smsmith
30119418Sobrien#include <sys/types.h>
31119418Sobrien#include <sys/errno.h>
32119418Sobrien#include <sys/socket.h>
3355205Speter#include <sys/sysctl.h>
3438061Smsmith
3538061Smsmith#include <arpa/inet.h>
3655939Snsouch#include <netinet/in.h>
3755939Snsouch
3838061Smsmith#include <err.h>
3938061Smsmith#include <netdb.h>
4038061Smsmith#include <stdio.h>
4155205Speter#include <stdlib.h>
4238061Smsmith#include <string.h>
4342475Snsouch#include <unistd.h>
4442475Snsouch
4555939Snsouch#include "jailp.h"
4638061Smsmith
4738061Smsmithstruct ipspec {
4838061Smsmith	const char	*name;
4938061Smsmith	unsigned	flags;
5055939Snsouch};
5155939Snsouch
5238061Smsmithextern FILE *yyin;
5338061Smsmithextern int yynerrs;
5438061Smsmith
5538061Smsmithextern int yyparse(void);
5638061Smsmith
5738061Smsmithstruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
5838061Smsmith
5938061Smsmithstatic void free_param(struct cfparams *pp, struct cfparam *p);
6038061Smsmithstatic void free_param_strings(struct cfparam *p);
6138061Smsmith
6238061Smsmithstatic const struct ipspec intparams[] = {
6338061Smsmith    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
6438061Smsmith    [IP_COMMAND] =		{"command",		PF_INTERNAL},
6538061Smsmith    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
6638061Smsmith    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
6738061Smsmith    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
6838061Smsmith    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
6938061Smsmith    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
7038061Smsmith    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
7138061Smsmith    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
7238061Smsmith    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
7338061Smsmith    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
7438061Smsmith    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
7538061Smsmith    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
7638061Smsmith    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
7738061Smsmith							PF_INTERNAL | PF_BOOL},
7838061Smsmith    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
7938061Smsmith    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
8038061Smsmith#if defined(INET) || defined(INET6)
8138061Smsmith    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
8238061Smsmith    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
8338061Smsmith#endif
8439134Snsouch    [IP_MOUNT] =		{"mount",		PF_INTERNAL | PF_REV},
8539134Snsouch    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
8638061Smsmith    [IP_MOUNT_FDESCFS] =	{"mount.fdescfs",	PF_INTERNAL | PF_BOOL},
8738061Smsmith    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
8838061Smsmith    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
8938061Smsmith    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
9038061Smsmith#ifdef INET
9138061Smsmith    [IP__IP4_IFADDR] =		{"ip4.addr",	PF_INTERNAL | PF_CONV | PF_REV},
9238061Smsmith#endif
9338061Smsmith#ifdef INET6
9438061Smsmith    [IP__IP6_IFADDR] =		{"ip6.addr",	PF_INTERNAL | PF_CONV | PF_REV},
9538061Smsmith#endif
9638061Smsmith    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",	PF_INTERNAL | PF_CONV | PF_REV},
9738061Smsmith    [IP__OP] =			{NULL,			PF_CONV},
9838061Smsmith    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
9978645Snsouch    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
10078645Snsouch    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
10178645Snsouch    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
10278645Snsouch    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
10378645Snsouch    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
10478645Snsouch    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
10578645Snsouch    [KP_ENFORCE_STATFS] =	{"enforce_statfs",	0},
10678645Snsouch    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
10778645Snsouch#ifdef INET
10878645Snsouch    [KP_IP4_ADDR] =		{"ip4.addr",		0},
10978645Snsouch#endif
11078645Snsouch#ifdef INET6
11178645Snsouch    [KP_IP6_ADDR] =		{"ip6.addr",		0},
11278645Snsouch#endif
11378645Snsouch    [KP_JID] =			{"jid",			0},
11439134Snsouch    [KP_NAME] =			{"name",		0},
11539134Snsouch    [KP_PATH] =			{"path",		0},
11639134Snsouch    [KP_PERSIST] =		{"persist",		0},
11739134Snsouch    [KP_SECURELEVEL] =		{"securelevel",		0},
11845342Speter    [KP_VNET] =			{"vnet",		0},
11939134Snsouch};
12045342Speter
12139134Snsouch/*
12239134Snsouch * Parse the jail configuration file.
12339134Snsouch */
12439134Snsouchvoid
12539134Snsouchload_config(void)
12638061Smsmith{
12738061Smsmith	struct cfjails wild;
12838061Smsmith	struct cfparams opp;
12938061Smsmith	struct cfjail *j, *tj, *wj;
13038061Smsmith	struct cfparam *p, *vp, *tp;
13138061Smsmith	struct cfstring *s, *vs, *ns;
13238061Smsmith	struct cfvar *v;
13339134Snsouch	char *ep;
13439134Snsouch	size_t varoff;
13538061Smsmith	int did_self, jseq, pgen;
13638061Smsmith
13738061Smsmith	if (!strcmp(cfname, "-")) {
13838061Smsmith		cfname = "STDIN";
13938061Smsmith		yyin = stdin;
14038061Smsmith	} else {
14138061Smsmith		yyin = fopen(cfname, "r");
14239134Snsouch		if (!yyin)
14338061Smsmith			err(1, "%s", cfname);
14438061Smsmith	}
14538061Smsmith	if (yyparse() || yynerrs)
14638061Smsmith		exit(1);
14739134Snsouch
14838061Smsmith	/* Separate the wildcard jails out from the actual jails. */
14938061Smsmith	jseq = 0;
15038061Smsmith	TAILQ_INIT(&wild);
15138061Smsmith	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
15239134Snsouch		j->seq = ++jseq;
15338061Smsmith		if (wild_jail_name(j->name))
15438061Smsmith			requeue(j, &wild);
15538061Smsmith	}
15638061Smsmith
15738061Smsmith	TAILQ_FOREACH(j, &cfjails, tq) {
15838061Smsmith		/* Set aside the jail's parameters. */
15938061Smsmith		TAILQ_INIT(&opp);
16038061Smsmith		TAILQ_CONCAT(&opp, &j->params, tq);
16138061Smsmith		/*
16238061Smsmith		 * The jail name implies its "name" or "jid" parameter,
16338061Smsmith		 * though they may also be explicitly set later on.
16438061Smsmith		 */
16538061Smsmith		add_param(j, NULL,
16638061Smsmith		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
16738061Smsmith		    j->name);
16838061Smsmith		/*
16938061Smsmith		 * Collect parameters for the jail, global parameters/variables,
17038061Smsmith		 * and any matching wildcard jails.
17138061Smsmith		 */
17238061Smsmith		did_self = 0;
17378645Snsouch		TAILQ_FOREACH(wj, &wild, tq) {
17478645Snsouch			if (j->seq < wj->seq && !did_self) {
17578645Snsouch				TAILQ_FOREACH(p, &opp, tq)
17678645Snsouch					add_param(j, p, 0, NULL);
17738061Smsmith				did_self = 1;
17838061Smsmith			}
17938061Smsmith			if (wild_jail_match(j->name, wj->name))
18038061Smsmith				TAILQ_FOREACH(p, &wj->params, tq)
18138061Smsmith					add_param(j, p, 0, NULL);
18238061Smsmith		}
18338061Smsmith		if (!did_self)
18478645Snsouch			TAILQ_FOREACH(p, &opp, tq)
18578645Snsouch				add_param(j, p, 0, NULL);
18678645Snsouch
18778645Snsouch		/* Resolve any variable substitutions. */
18878645Snsouch		pgen = 0;
18978645Snsouch		TAILQ_FOREACH(p, &j->params, tq) {
19078645Snsouch		    p->gen = ++pgen;
19178645Snsouch		find_vars:
19278645Snsouch		    TAILQ_FOREACH(s, &p->val, tq) {
19378645Snsouch			varoff = 0;
19478645Snsouch			while ((v = STAILQ_FIRST(&s->vars))) {
19578645Snsouch				TAILQ_FOREACH(vp, &j->params, tq)
19678645Snsouch					if (!strcmp(vp->name, v->name))
19778645Snsouch						break;
19838061Smsmith				if (!vp) {
19938061Smsmith					jail_warnx(j,
20038061Smsmith					    "%s: variable \"%s\" not found",
20138061Smsmith					    p->name, v->name);
20239134Snsouch				bad_var:
20338061Smsmith					j->flags |= JF_FAILED;
20438061Smsmith					TAILQ_FOREACH(vp, &j->params, tq)
20538061Smsmith						if (vp->gen == pgen)
20638061Smsmith							vp->flags |= PF_BAD;
20738061Smsmith					goto free_var;
20838061Smsmith				}
20943433Snsouch				if (vp->flags & PF_BAD)
21038061Smsmith					goto bad_var;
21138061Smsmith				if (vp->gen == pgen) {
21238061Smsmith					jail_warnx(j, "%s: variable loop",
21338061Smsmith					    v->name);
21438061Smsmith					goto bad_var;
21538061Smsmith				}
21638061Smsmith				TAILQ_FOREACH(vs, &vp->val, tq)
21738061Smsmith					if (!STAILQ_EMPTY(&vs->vars)) {
21839134Snsouch						vp->gen = pgen;
21938061Smsmith						TAILQ_REMOVE(&j->params, vp,
22038061Smsmith						    tq);
221185003Sjhb						TAILQ_INSERT_BEFORE(p, vp, tq);
22238061Smsmith						p = vp;
22338061Smsmith						goto find_vars;
22438061Smsmith					}
22543433Snsouch				vs = TAILQ_FIRST(&vp->val);
22638061Smsmith				if (TAILQ_NEXT(vs, tq) != NULL &&
22738061Smsmith				    (s->s[0] != '\0' ||
22838061Smsmith				     STAILQ_NEXT(v, tq))) {
22938061Smsmith					jail_warnx(j, "%s: array cannot be "
23038061Smsmith					    "substituted inline",
23138061Smsmith					    p->name);
23239134Snsouch					goto bad_var;
23338061Smsmith				}
23438061Smsmith				s->s = erealloc(s->s, s->len + vs->len + 1);
23538061Smsmith				memmove(s->s + v->pos + varoff + vs->len,
236185003Sjhb				    s->s + v->pos + varoff,
23743433Snsouch				    s->len - (v->pos + varoff) + 1);
23843433Snsouch				memcpy(s->s + v->pos + varoff, vs->s, vs->len);
23938061Smsmith				varoff += vs->len;
24038061Smsmith				s->len += vs->len;
24138061Smsmith				while ((vs = TAILQ_NEXT(vs, tq))) {
24238061Smsmith					ns = emalloc(sizeof(struct cfstring));
24338061Smsmith					ns->s = estrdup(vs->s);
24438061Smsmith					ns->len = vs->len;
24538061Smsmith					STAILQ_INIT(&ns->vars);
24638061Smsmith					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
24739134Snsouch					s = ns;
24838061Smsmith				}
24938061Smsmith			free_var:
25038061Smsmith				free(v->name);
251185003Sjhb				STAILQ_REMOVE_HEAD(&s->vars, tq);
25243433Snsouch				free(v);
25343433Snsouch			}
25438061Smsmith		    }
25538061Smsmith		}
25638061Smsmith
25738061Smsmith		/* Free the jail's original parameter list and any variables. */
25838061Smsmith		while ((p = TAILQ_FIRST(&opp)))
25938061Smsmith			free_param(&opp, p);
26038061Smsmith		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
26138061Smsmith			if (p->flags & PF_VAR)
26239134Snsouch				free_param(&j->params, p);
26339134Snsouch	}
26439134Snsouch	while ((wj = TAILQ_FIRST(&wild))) {
26539134Snsouch		free(wj->name);
26643433Snsouch		while ((p = TAILQ_FIRST(&wj->params)))
26739134Snsouch			free_param(&wj->params, p);
26843433Snsouch		TAILQ_REMOVE(&wild, wj, tq);
26939134Snsouch	}
27039520Snsouch}
27139134Snsouch
27239520Snsouch/*
27339134Snsouch * Create a new jail record.
27439134Snsouch */
27538061Smsmithstruct cfjail *
27638061Smsmithadd_jail(void)
27738061Smsmith{
27855939Snsouch	struct cfjail *j;
27938061Smsmith
28038061Smsmith	j = emalloc(sizeof(struct cfjail));
28155939Snsouch	memset(j, 0, sizeof(struct cfjail));
28255939Snsouch	TAILQ_INIT(&j->params);
28338061Smsmith	STAILQ_INIT(&j->dep[DEP_FROM]);
28438061Smsmith	STAILQ_INIT(&j->dep[DEP_TO]);
28538061Smsmith	j->queue = &cfjails;
28638061Smsmith	TAILQ_INSERT_TAIL(&cfjails, j, tq);
28738061Smsmith	return j;
28838061Smsmith}
28938061Smsmith
29038061Smsmith/*
29155939Snsouch * Add a parameter to a jail.
29238061Smsmith */
29338061Smsmithvoid
29438061Smsmithadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
29555939Snsouch    const char *value)
29642475Snsouch{
29742475Snsouch	struct cfstrings nss;
29887599Sobrien	struct cfparam *dp, *np;
29942475Snsouch	struct cfstring *s, *ns;
300185003Sjhb	struct cfvar *v, *nv;
30142475Snsouch	const char *name;
30238061Smsmith	char *cs, *tname;
30355939Snsouch	unsigned flags;
30455939Snsouch
30538061Smsmith	if (j == NULL) {
30655939Snsouch		/* Create a single anonymous jail if one doesn't yet exist. */
30738061Smsmith		j = TAILQ_LAST(&cfjails, cfjails);
30838061Smsmith		if (j == NULL)
30938061Smsmith			j = add_jail();
31038061Smsmith	}
31138061Smsmith	TAILQ_INIT(&nss);
31239134Snsouch	if (p != NULL) {
31338061Smsmith		name = p->name;
31439134Snsouch		flags = p->flags;
31538061Smsmith		/*
31639134Snsouch		 * Make a copy of the parameter's string list,
317185003Sjhb		 * which may be freed if it's overridden later.
31838061Smsmith		 */
31955939Snsouch		TAILQ_FOREACH(s, &p->val, tq) {
32039134Snsouch			ns = emalloc(sizeof(struct cfstring));
32138061Smsmith			ns->s = estrdup(s->s);
32239134Snsouch			ns->len = s->len;
32338061Smsmith			STAILQ_INIT(&ns->vars);
32439134Snsouch			STAILQ_FOREACH(v, &s->vars, tq) {
32538061Smsmith				nv = emalloc(sizeof(struct cfvar));
32639134Snsouch				nv->name = strdup(v->name);
32739134Snsouch				nv->pos = v->pos;
32839134Snsouch				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
32939134Snsouch			}
33039134Snsouch			TAILQ_INSERT_TAIL(&nss, ns, tq);
33139134Snsouch		}
33238061Smsmith	} else {
33339134Snsouch		flags = PF_APPEND;
33455939Snsouch		if (ipnum != IP__NULL) {
33539134Snsouch			name = intparams[ipnum].name;
33639134Snsouch			flags |= intparams[ipnum].flags;
33738061Smsmith		} else if ((cs = strchr(value, '='))) {
33838061Smsmith			tname = alloca(cs - value + 1);
33938061Smsmith			strlcpy(tname, value, cs - value + 1);
34039134Snsouch			name = tname;
34138061Smsmith			value = cs + 1;
34239134Snsouch		} else {
34339134Snsouch			name = value;
34438061Smsmith			value = NULL;
34555939Snsouch		}
34639134Snsouch		if (value != NULL) {
34738061Smsmith			ns = emalloc(sizeof(struct cfstring));
34855939Snsouch			ns->s = estrdup(value);
34938061Smsmith			ns->len = strlen(value);
35039134Snsouch			STAILQ_INIT(&ns->vars);
35138061Smsmith			TAILQ_INSERT_TAIL(&nss, ns, tq);
35238061Smsmith		}
35338061Smsmith	}
35438061Smsmith
35538061Smsmith	/* See if this parameter has already been added. */
35638061Smsmith	if (ipnum != IP__NULL)
35738061Smsmith		dp = j->intparams[ipnum];
35839134Snsouch	else
35938061Smsmith		TAILQ_FOREACH(dp, &j->params, tq)
36038061Smsmith			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
36155939Snsouch				break;
36239520Snsouch	if (dp != NULL) {
36338061Smsmith		/* Found it - append or replace. */
36439520Snsouch		if (strcmp(dp->name, name)) {
36555939Snsouch			free(dp->name);
366185003Sjhb			dp->name = estrdup(name);
36739520Snsouch		}
36878645Snsouch		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
36955939Snsouch			free_param_strings(dp);
37039520Snsouch		TAILQ_CONCAT(&dp->val, &nss, tq);
37170608Snsouch		dp->flags |= flags;
37270608Snsouch	} else {
37370608Snsouch		/* Not found - add it. */
37470608Snsouch		np = emalloc(sizeof(struct cfparam));
37555939Snsouch		np->name = estrdup(name);
37670608Snsouch		TAILQ_INIT(&np->val);
37739520Snsouch		TAILQ_CONCAT(&np->val, &nss, tq);
37870608Snsouch		np->flags = flags;
37970608Snsouch		np->gen = 0;
38070608Snsouch		TAILQ_INSERT_TAIL(&j->params, np, tq);
38139520Snsouch		if (ipnum != IP__NULL)
38270608Snsouch			j->intparams[ipnum] = np;
38370608Snsouch		else
38470608Snsouch			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
38570608Snsouch				if (!(intparams[ipnum].flags & PF_CONV) &&
38670608Snsouch				    equalopts(name, intparams[ipnum].name)) {
38739520Snsouch					j->intparams[ipnum] = np;
38870608Snsouch					np->flags |= intparams[ipnum].flags;
38970608Snsouch					break;
39070608Snsouch				}
39170608Snsouch	}
39270608Snsouch}
39370608Snsouch
39470608Snsouch/*
39570608Snsouch * Return if a boolean parameter exists and is true.
39670608Snsouch */
39770608Snsouchint
39855939Snsouchbool_param(const struct cfparam *p)
39970608Snsouch{
40070608Snsouch	const char *cs;
40139520Snsouch
402184130Sjhb	if (p == NULL)
403184130Sjhb		return 0;
40439520Snsouch	cs = strrchr(p->name, '.');
40539520Snsouch	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
40655939Snsouch	    (TAILQ_EMPTY(&p->val) ||
40739520Snsouch	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
40839520Snsouch	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
40939520Snsouch}
41070608Snsouch
41170608Snsouch/*
41238061Smsmith * Set an integer if a parameter if it exists.
41338061Smsmith */
41438061Smsmithint
41538061Smsmithint_param(const struct cfparam *p, int *ip)
41638061Smsmith{
41755939Snsouch	if (p == NULL || TAILQ_EMPTY(&p->val))
41838061Smsmith		return 0;
419185003Sjhb	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
42039134Snsouch	return 1;
42170608Snsouch}
42239520Snsouch
423184130Sjhb/*
424184130Sjhb * Return the string value of a scalar parameter if it exists.
42539520Snsouch */
42639520Snsouchconst char *
42738061Smsmithstring_param(const struct cfparam *p)
42855939Snsouch{
42938061Smsmith	return (p && !TAILQ_EMPTY(&p->val)
43039520Snsouch	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
43139520Snsouch}
43255939Snsouch
43339520Snsouch/*
43438061Smsmith * Check syntax and values of internal parameters.  Set some internal
43538061Smsmith * parameters based on the values of others.
43638061Smsmith */
43738061Smsmithint
43838061Smsmithcheck_intparams(struct cfjail *j)
43938061Smsmith{
44038061Smsmith	struct cfparam *p;
44138061Smsmith	struct cfstring *s;
44255939Snsouch	FILE *f;
44338061Smsmith	const char *val;
44438061Smsmith	char *cs, *ep, *ln;
44555939Snsouch	size_t lnlen;
44645342Speter	int error;
44738061Smsmith#if defined(INET) || defined(INET6)
44855939Snsouch	struct addrinfo hints;
44938061Smsmith	struct addrinfo *ai0, *ai;
45038061Smsmith	const char *hostname;
45138061Smsmith	int gicode, defif, prefix;
45238061Smsmith#endif
45338061Smsmith#ifdef INET
45438061Smsmith	struct in_addr addr4;
45538061Smsmith	int ip4ok;
45638061Smsmith	char avalue4[INET_ADDRSTRLEN];
45738061Smsmith#endif
45838061Smsmith#ifdef INET6
45955939Snsouch	struct in6_addr addr6;
46038061Smsmith	int ip6ok;
46138061Smsmith	char avalue6[INET6_ADDRSTRLEN];
46255939Snsouch#endif
46345342Speter
46438061Smsmith	error = 0;
46555939Snsouch	/* Check format of boolan and integer values. */
46638061Smsmith	TAILQ_FOREACH(p, &j->params, tq) {
46738061Smsmith		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
46838061Smsmith			val = TAILQ_LAST(&p->val, cfstrings)->s;
46938061Smsmith			if (p->flags & PF_BOOL) {
47038061Smsmith				if (strcasecmp(val, "false") &&
47138061Smsmith				    strcasecmp(val, "true") &&
47238061Smsmith				    ((void)strtol(val, &ep, 10), *ep)) {
47355939Snsouch					jail_warnx(j,
47438061Smsmith					    "%s: unknown boolean value \"%s\"",
47538061Smsmith					    p->name, val);
47638061Smsmith					error = -1;
47738061Smsmith				}
47838061Smsmith			} else {
47938061Smsmith				(void)strtol(val, &ep, 10);
48038061Smsmith				if (ep == val || *ep) {
48138061Smsmith					jail_warnx(j,
48238061Smsmith					    "%s: non-integer value \"%s\"",
48338061Smsmith					    p->name, val);
48438061Smsmith					error = -1;
48538061Smsmith				}
48638061Smsmith			}
48738061Smsmith		}
48838061Smsmith	}
48938061Smsmith
49038061Smsmith#if defined(INET) || defined(INET6)
49138061Smsmith	/*
49243433Snsouch	 * The ip_hostname parameter looks up the hostname, and adds parameters
49343433Snsouch	 * for any IP addresses it finds.
49438061Smsmith	 */
49538061Smsmith	if (((j->flags & JF_OP_MASK) != JF_STOP ||
49638061Smsmith	    j->intparams[IP_INTERFACE] != NULL) &&
49738061Smsmith	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
49838061Smsmith	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
49938061Smsmith		j->intparams[IP_IP_HOSTNAME] = NULL;
50038061Smsmith		/*
50138061Smsmith		 * Silently ignore unsupported address families from
502185003Sjhb		 * DNS lookups.
50355939Snsouch		 */
50438061Smsmith#ifdef INET
50538061Smsmith		ip4ok = feature_present("inet");
50638061Smsmith#endif
50738061Smsmith#ifdef INET6
50838061Smsmith		ip6ok = feature_present("inet6");
50938061Smsmith#endif
51038061Smsmith		if (
51138061Smsmith#if defined(INET) && defined(INET6)
51238061Smsmith		    ip4ok || ip6ok
51338061Smsmith#elif defined(INET)
51438061Smsmith		    ip4ok
51538061Smsmith#elif defined(INET6)
51638061Smsmith		    ip6ok
51738061Smsmith#endif
51838061Smsmith			 ) {
51938061Smsmith			/* Look up the hostname (or get the address) */
52038061Smsmith			memset(&hints, 0, sizeof(hints));
52178645Snsouch			hints.ai_socktype = SOCK_STREAM;
52278645Snsouch			hints.ai_family =
52355939Snsouch#if defined(INET) && defined(INET6)
52478645Snsouch			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
52538061Smsmith#elif defined(INET)
52638061Smsmith			    PF_INET;
52755939Snsouch#elif defined(INET6)
52838061Smsmith			    PF_INET6;
52938061Smsmith#endif
53055939Snsouch			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
53138061Smsmith			if (gicode != 0) {
53238061Smsmith				jail_warnx(j, "host.hostname %s: %s", hostname,
53338061Smsmith				    gai_strerror(gicode));
53438061Smsmith				error = -1;
53538061Smsmith			} else {
53638061Smsmith				/*
53738061Smsmith				 * Convert the addresses to ASCII so jailparam
53838061Smsmith				 * can convert them back.  Errors are not
53938061Smsmith				 * expected here.
54038061Smsmith				 */
54178645Snsouch				for (ai = ai0; ai; ai = ai->ai_next)
54278645Snsouch					switch (ai->ai_family) {
54378645Snsouch#ifdef INET
54478645Snsouch					case AF_INET:
54578645Snsouch						memcpy(&addr4,
54678645Snsouch						    &((struct sockaddr_in *)
54778645Snsouch						    (void *)ai->ai_addr)->
548185003Sjhb						    sin_addr, sizeof(addr4));
54978645Snsouch						if (inet_ntop(AF_INET,
55078645Snsouch						    &addr4, avalue4,
55138061Smsmith						    INET_ADDRSTRLEN) == NULL)
55238061Smsmith							err(1, "inet_ntop");
55338061Smsmith						add_param(j, NULL, KP_IP4_ADDR,
55438061Smsmith						    avalue4);
55538061Smsmith						break;
55638061Smsmith#endif
55738061Smsmith#ifdef INET6
55838061Smsmith					case AF_INET6:
55955939Snsouch						memcpy(&addr6,
56055939Snsouch						    &((struct sockaddr_in6 *)
56138061Smsmith						    (void *)ai->ai_addr)->
56255939Snsouch						    sin6_addr, sizeof(addr6));
56338061Smsmith						if (inet_ntop(AF_INET6,
56438061Smsmith						    &addr6, avalue6,
56555939Snsouch						    INET6_ADDRSTRLEN) == NULL)
56638061Smsmith							err(1, "inet_ntop");
56739134Snsouch						add_param(j, NULL, KP_IP6_ADDR,
56839134Snsouch						    avalue6);
56939134Snsouch						break;
57039134Snsouch#endif
57139134Snsouch					}
57238061Smsmith				freeaddrinfo(ai0);
57355939Snsouch			}
57455939Snsouch		}
57538061Smsmith	}
57638061Smsmith
57755939Snsouch	/*
57838061Smsmith	 * IP addresses may include an interface to set that address on,
57938061Smsmith	 * a netmask/suffix for that address and options for ifconfig.
58038061Smsmith	 * These are copied to an internal command parameter and then stripped
58138061Smsmith	 * so they won't be passed on to jailparam_set.
58238061Smsmith	 */
58338061Smsmith	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
58438061Smsmith#ifdef INET
58538061Smsmith	if (j->intparams[KP_IP4_ADDR] != NULL) {
58638061Smsmith		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
58738061Smsmith			cs = strchr(s->s, '|');
58838061Smsmith			if (cs || defif)
589185003Sjhb				add_param(j, NULL, IP__IP4_IFADDR, s->s);
59055939Snsouch			if (cs) {
59170608Snsouch				strcpy(s->s, cs + 1);
59238061Smsmith				s->len -= cs + 1 - s->s;
59338061Smsmith			}
59438061Smsmith			if ((cs = strchr(s->s, '/'))) {
59538061Smsmith				prefix = strtol(cs + 1, &ep, 10);
59638061Smsmith				if (*ep == '.'
59755939Snsouch				    ? inet_pton(AF_INET, cs + 1, &addr4) != 1
59838061Smsmith				    : *ep || prefix < 0 || prefix > 32) {
59938061Smsmith					jail_warnx(j,
60038061Smsmith					    "ip4.addr: bad netmask \"%s\"", cs);
60138061Smsmith					error = -1;
60238061Smsmith				}
60378645Snsouch				*cs = '\0';
60478645Snsouch				s->len = cs - s->s;
60578645Snsouch			}
60678645Snsouch			if ((cs = strchr(s->s, ' ')) != NULL) {
607185003Sjhb				*cs = '\0';
60838061Smsmith				s->len = cs - s->s;
60938061Smsmith			}
61038061Smsmith		}
61138061Smsmith	}
612187576Sjhb#endif
61370608Snsouch#ifdef INET6
61470608Snsouch	if (j->intparams[KP_IP6_ADDR] != NULL) {
61538061Smsmith		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
61670608Snsouch			cs = strchr(s->s, '|');
61770608Snsouch			if (cs || defif)
61870608Snsouch				add_param(j, NULL, IP__IP6_IFADDR, s->s);
61970608Snsouch			if (cs) {
62070608Snsouch				strcpy(s->s, cs + 1);
621184130Sjhb				s->len -= cs + 1 - s->s;
62270608Snsouch			}
62370608Snsouch			if ((cs = strchr(s->s, '/'))) {
62455939Snsouch				prefix = strtol(cs + 1, &ep, 10);
62555939Snsouch				if (*ep || prefix < 0 || prefix > 128) {
626184130Sjhb					jail_warnx(j,
62770608Snsouch					    "ip6.addr: bad prefixlen \"%s\"",
62870608Snsouch					    cs);
62970608Snsouch					error = -1;
63070608Snsouch				}
631184130Sjhb				*cs = '\0';
63270608Snsouch				s->len = cs - s->s;
63370608Snsouch			}
63470608Snsouch			if ((cs = strchr(s->s, ' ')) != NULL) {
63538061Smsmith				*cs = '\0';
63638061Smsmith				s->len = cs - s->s;
63755939Snsouch			}
63838061Smsmith		}
63970608Snsouch	}
640187576Sjhb#endif
64170608Snsouch#endif
64238061Smsmith
64338061Smsmith	/*
64438061Smsmith	 * Read mount.fstab file(s), and treat each line as its own mount
64538061Smsmith	 * parameter.
64638061Smsmith	 */
64738061Smsmith	if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
64838061Smsmith		TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
64938061Smsmith			if (s->len == 0)
65038061Smsmith				continue;
65138061Smsmith			f = fopen(s->s, "r");
65239520Snsouch			if (f == NULL) {
65342475Snsouch				jail_warnx(j, "mount.fstab: %s: %s",
65442475Snsouch				    s->s, strerror(errno));
65587599Sobrien				error = -1;
65642475Snsouch				continue;
65738061Smsmith			}
65838061Smsmith			while ((ln = fgetln(f, &lnlen))) {
65938061Smsmith				if ((cs = memchr(ln, '#', lnlen - 1)))
66038061Smsmith					lnlen = cs - ln + 1;
66138061Smsmith				if (ln[lnlen - 1] == '\n' ||
66238061Smsmith				    ln[lnlen - 1] == '#')
66338061Smsmith					ln[lnlen - 1] = '\0';
66438061Smsmith				else {
66538061Smsmith					cs = alloca(lnlen + 1);
66638061Smsmith					strlcpy(cs, ln, lnlen + 1);
66738061Smsmith					ln = cs;
66838061Smsmith				}
66938061Smsmith				add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
67038061Smsmith			}
67138061Smsmith			fclose(f);
67238061Smsmith		}
67338061Smsmith	}
67438061Smsmith	if (error)
67538061Smsmith		failed(j);
67638061Smsmith	return error;
677185003Sjhb}
67838061Smsmith
67938061Smsmith/*
68038061Smsmith * Import parameters into libjail's binary jailparam format.
68138061Smsmith */
68255939Snsouchint
68338061Smsmithimport_params(struct cfjail *j)
68438061Smsmith{
68538061Smsmith	struct cfparam *p;
68638061Smsmith	struct cfstring *s, *ts;
68738061Smsmith	struct jailparam *jp;
68838061Smsmith	char *value, *cs;
68938061Smsmith	size_t vallen;
69038061Smsmith	int error;
69138061Smsmith
69238061Smsmith	error = 0;
69338061Smsmith	j->njp = 0;
69438061Smsmith	TAILQ_FOREACH(p, &j->params, tq)
69538061Smsmith		if (!(p->flags & PF_INTERNAL))
69638061Smsmith			j->njp++;
69738061Smsmith	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
69838061Smsmith	TAILQ_FOREACH(p, &j->params, tq) {
69939520Snsouch		if (p->flags & PF_INTERNAL)
700185003Sjhb			continue;
701185003Sjhb		if (jailparam_init(jp, p->name) < 0) {
70238061Smsmith			error = -1;
70338061Smsmith			jail_warnx(j, "%s", jail_errmsg);
70438061Smsmith			jp++;
70538061Smsmith			continue;
70638061Smsmith		}
70738061Smsmith		if (TAILQ_EMPTY(&p->val))
70838061Smsmith			value = NULL;
70938061Smsmith		else if (!jp->jp_elemlen ||
71038061Smsmith			 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
71138061Smsmith			/*
71255939Snsouch			 * Scalar parameters silently discard multiple (array)
71338061Smsmith			 * values, keeping only the last value added.  This
71438061Smsmith			 * lets values added from the command line append to
71538061Smsmith			 * arrays wthout pre-checking the type.
71638061Smsmith			 */
71738061Smsmith			value = TAILQ_LAST(&p->val, cfstrings)->s;
71838061Smsmith		} else {
71938061Smsmith			/*
72038061Smsmith			 * Convert arrays into comma-separated strings, which
72138061Smsmith			 * jailparam_import will then convert back into arrays.
72238061Smsmith			 */
72338061Smsmith			vallen = 0;
72438061Smsmith			TAILQ_FOREACH(s, &p->val, tq)
725185003Sjhb				vallen += s->len + 1;
726185003Sjhb			value = alloca(vallen);
72738061Smsmith			cs = value;
72838061Smsmith			TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
72938061Smsmith				memcpy(cs, s->s, s->len);
73038061Smsmith				cs += s->len + 1;
73138061Smsmith				cs[-1] = ',';
73238061Smsmith			}
733185003Sjhb			value[vallen - 1] = '\0';
734185003Sjhb		}
73538061Smsmith		if (jailparam_import(jp, value) < 0) {
73638061Smsmith			error = -1;
73738061Smsmith			jail_warnx(j, "%s", jail_errmsg);
73838061Smsmith		}
73938061Smsmith		jp++;
74038061Smsmith	}
74138061Smsmith	if (error) {
74238061Smsmith		jailparam_free(j->jp, j->njp);
74338061Smsmith		free(j->jp);
74438061Smsmith		j->jp = NULL;
74538061Smsmith		failed(j);
74639520Snsouch	}
74739520Snsouch	return error;
74839520Snsouch}
74955939Snsouch
75039520Snsouch/*
75139520Snsouch * Check if options are equal (with or without the "no" prefix).
75239520Snsouch */
75339520Snsouchint
75439520Snsouchequalopts(const char *opt1, const char *opt2)
75538061Smsmith{
75638061Smsmith	char *p;
75738061Smsmith
75838061Smsmith	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
75938061Smsmith	if (strcmp(opt1, opt2) == 0)
76038061Smsmith		return (1);
76138061Smsmith	/* "noopt" vs. "opt" */
76238061Smsmith	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
76338061Smsmith		return (1);
76438061Smsmith	/* "opt" vs. "noopt" */
76538061Smsmith	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
76638061Smsmith		return (1);
76738061Smsmith	while ((p = strchr(opt1, '.')) != NULL &&
76838061Smsmith	    !strncmp(opt1, opt2, ++p - opt1)) {
76938061Smsmith		opt2 += p - opt1;
770185003Sjhb		opt1 = p;
771185003Sjhb		/* "foo.noopt" vs. "foo.opt" */
77238061Smsmith		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
77338061Smsmith			return (1);
77438061Smsmith		/* "foo.opt" vs. "foo.noopt" */
77538061Smsmith		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
77638061Smsmith			return (1);
777185003Sjhb	}
778185003Sjhb	return (0);
77938061Smsmith}
78038061Smsmith
78138061Smsmith/*
78238061Smsmith * See if a jail name matches a wildcard.
78338061Smsmith */
78438061Smsmithint
78538061Smsmithwild_jail_match(const char *jname, const char *wname)
78638061Smsmith{
78738061Smsmith	const char *jc, *jd, *wc, *wd;
788
789	/*
790	 * A non-final "*" component in the wild name matches a single jail
791	 * component, and a final "*" matches one or more jail components.
792	 */
793	for (jc = jname, wc = wname;
794	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
795	     jc = jd + 1, wc = wd + 1)
796		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
797			return 0;
798	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
799}
800
801/*
802 * Return if a jail name is a wildcard.
803 */
804int
805wild_jail_name(const char *wname)
806{
807	const char *wc;
808
809	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
810		if ((wc == wname || wc[-1] == '.') &&
811		    (wc[1] == '\0' || wc[1] == '.'))
812			return 1;
813	return 0;
814}
815
816/*
817 * Free a parameter record and all its strings and variables.
818 */
819static void
820free_param(struct cfparams *pp, struct cfparam *p)
821{
822	free(p->name);
823	free_param_strings(p);
824	TAILQ_REMOVE(pp, p, tq);
825	free(p);
826}
827
828static void
829free_param_strings(struct cfparam *p)
830{
831	struct cfstring *s;
832	struct cfvar *v;
833
834	while ((s = TAILQ_FIRST(&p->val))) {
835		free(s->s);
836		while ((v = STAILQ_FIRST(&s->vars))) {
837			free(v->name);
838			STAILQ_REMOVE_HEAD(&s->vars, tq);
839			free(v);
840		}
841		TAILQ_REMOVE(&p->val, s, tq);
842		free(s);
843	}
844}
845