config.c revision 278484
155714Skris/*-
255714Skris * Copyright (c) 2011 James Gritton
355714Skris * All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
8280304Sjkim * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris *
1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15280304Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22280304Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455714Skris * SUCH DAMAGE.
2555714Skris */
2655714Skris
2755714Skris#include <sys/cdefs.h>
2855714Skris__FBSDID("$FreeBSD: stable/10/usr.sbin/jail/config.c 278484 2015-02-10 01:05:51Z jamie $");
2955714Skris
3055714Skris#include <sys/types.h>
3155714Skris#include <sys/errno.h>
3255714Skris#include <sys/socket.h>
3355714Skris#include <sys/sysctl.h>
3455714Skris
3555714Skris#include <arpa/inet.h>
3655714Skris#include <netinet/in.h>
37280304Sjkim
3855714Skris#include <err.h>
3955714Skris#include <netdb.h>
40280304Sjkim#include <stdio.h>
4155714Skris#include <stdlib.h>
4255714Skris#include <string.h>
4355714Skris#include <unistd.h>
4455714Skris
4555714Skris#include "jailp.h"
4655714Skris
4755714Skrisstruct ipspec {
4855714Skris	const char	*name;
4955714Skris	unsigned	flags;
5055714Skris};
5155714Skris
52280304Sjkimextern FILE *yyin;
5355714Skrisextern int yynerrs;
5455714Skris
5555714Skrisextern int yyparse(void);
5655714Skris
5755714Skrisstruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
5855714Skris
5955714Skrisstatic void free_param(struct cfparams *pp, struct cfparam *p);
6055714Skrisstatic void free_param_strings(struct cfparam *p);
6155714Skris
6255714Skrisstatic const struct ipspec intparams[] = {
6359191Skris    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
6455714Skris    [IP_COMMAND] =		{"command",		PF_INTERNAL},
6555714Skris    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
6655714Skris    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
6755714Skris    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
6855714Skris    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
6955714Skris    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
7059191Skris    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
7155714Skris    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
7255714Skris    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
73238405Sjkim    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
74238405Sjkim    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
75238405Sjkim    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
76238405Sjkim    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
77280304Sjkim							PF_INTERNAL | PF_BOOL},
78238405Sjkim    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
79238405Sjkim    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
80238405Sjkim#if defined(INET) || defined(INET6)
81280304Sjkim    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
82238405Sjkim    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
83238405Sjkim#endif
84238405Sjkim    [IP_MOUNT] =		{"mount",		PF_INTERNAL | PF_REV},
85280304Sjkim    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
86238405Sjkim    [IP_MOUNT_FDESCFS] =	{"mount.fdescfs",	PF_INTERNAL | PF_BOOL},
87238405Sjkim    [IP_MOUNT_PROCFS] =		{"mount.procfs",	PF_INTERNAL | PF_BOOL},
88238405Sjkim    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
89280304Sjkim    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
90238405Sjkim    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
91238405Sjkim#ifdef INET
92238405Sjkim    [IP__IP4_IFADDR] =		{"ip4.addr",	PF_INTERNAL | PF_CONV | PF_REV},
93238405Sjkim#endif
94238405Sjkim#ifdef INET6
95238405Sjkim    [IP__IP6_IFADDR] =		{"ip6.addr",	PF_INTERNAL | PF_CONV | PF_REV},
96238405Sjkim#endif
97280304Sjkim    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",	PF_INTERNAL | PF_CONV | PF_REV},
98238405Sjkim    [IP__OP] =			{NULL,			PF_CONV},
99238405Sjkim    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
100238405Sjkim    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
101280304Sjkim    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
102238405Sjkim    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
103238405Sjkim    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
104238405Sjkim    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
105280304Sjkim    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
106238405Sjkim    [KP_ENFORCE_STATFS] =	{"enforce_statfs",	0},
107238405Sjkim    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
108238405Sjkim#ifdef INET
109280304Sjkim    [KP_IP4_ADDR] =		{"ip4.addr",		0},
110238405Sjkim#endif
111280304Sjkim#ifdef INET6
11268651Skris    [KP_IP6_ADDR] =		{"ip6.addr",		0},
11368651Skris#endif
114160814Ssimon    [KP_JID] =			{"jid",			0},
115238405Sjkim    [KP_NAME] =			{"name",		0},
11659191Skris    [KP_PATH] =			{"path",		0},
117109998Smarkm    [KP_PERSIST] =		{"persist",		0},
118109998Smarkm    [KP_SECURELEVEL] =		{"securelevel",		0},
119160814Ssimon    [KP_VNET] =			{"vnet",		0},
120238405Sjkim};
121238405Sjkim
122280304Sjkim/*
123238405Sjkim * Parse the jail configuration file.
124280304Sjkim */
125280304Sjkimvoid
126280304Sjkimload_config(void)
127280304Sjkim{
128280304Sjkim	struct cfjails wild;
129280304Sjkim	struct cfparams opp;
130238405Sjkim	struct cfjail *j, *tj, *wj;
131280304Sjkim	struct cfparam *p, *vp, *tp;
132238405Sjkim	struct cfstring *s, *vs, *ns;
133238405Sjkim	struct cfvar *v;
134280304Sjkim	char *ep;
135280304Sjkim	size_t varoff;
136238405Sjkim	int did_self, jseq, pgen;
13755714Skris
138280304Sjkim	if (!strcmp(cfname, "-")) {
13955714Skris		cfname = "STDIN";
14055714Skris		yyin = stdin;
141280304Sjkim	} else {
142280304Sjkim		yyin = fopen(cfname, "r");
143280304Sjkim		if (!yyin)
14455714Skris			err(1, "%s", cfname);
14555714Skris	}
14655714Skris	if (yyparse() || yynerrs)
147280304Sjkim		exit(1);
148280304Sjkim
149280304Sjkim	/* Separate the wildcard jails out from the actual jails. */
15055714Skris	jseq = 0;
15155714Skris	TAILQ_INIT(&wild);
15255714Skris	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
153280304Sjkim		j->seq = ++jseq;
154284285Sjkim		if (wild_jail_name(j->name))
155280304Sjkim			requeue(j, &wild);
156280304Sjkim	}
157280304Sjkim
158284285Sjkim	TAILQ_FOREACH(j, &cfjails, tq) {
159280304Sjkim		/* Set aside the jail's parameters. */
160280304Sjkim		TAILQ_INIT(&opp);
161280304Sjkim		TAILQ_CONCAT(&opp, &j->params, tq);
162280304Sjkim		/*
163306196Sjkim		 * The jail name implies its "name" or "jid" parameter,
164280304Sjkim		 * though they may also be explicitly set later on.
165280304Sjkim		 */
166285330Sjkim		add_param(j, NULL,
167285330Sjkim		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
168285330Sjkim		    j->name);
169285330Sjkim		/*
170285330Sjkim		 * Collect parameters for the jail, global parameters/variables,
171285330Sjkim		 * and any matching wildcard jails.
172306196Sjkim		 */
173285330Sjkim		did_self = 0;
174285330Sjkim		TAILQ_FOREACH(wj, &wild, tq) {
17555714Skris			if (j->seq < wj->seq && !did_self) {
176280304Sjkim				TAILQ_FOREACH(p, &opp, tq)
17755714Skris					add_param(j, p, 0, NULL);
178280304Sjkim				did_self = 1;
179280304Sjkim			}
180280304Sjkim			if (wild_jail_match(j->name, wj->name))
181280304Sjkim				TAILQ_FOREACH(p, &wj->params, tq)
182285330Sjkim					add_param(j, p, 0, NULL);
183285330Sjkim		}
184285330Sjkim		if (!did_self)
185306196Sjkim			TAILQ_FOREACH(p, &opp, tq)
186306196Sjkim				add_param(j, p, 0, NULL);
187285330Sjkim
188280304Sjkim		/* Resolve any variable substitutions. */
189285330Sjkim		pgen = 0;
190285330Sjkim		TAILQ_FOREACH(p, &j->params, tq) {
19155714Skris		    p->gen = ++pgen;
192280304Sjkim		find_vars:
193280304Sjkim		    TAILQ_FOREACH(s, &p->val, tq) {
194280304Sjkim			varoff = 0;
195280304Sjkim			while ((v = STAILQ_FIRST(&s->vars))) {
196306196Sjkim				TAILQ_FOREACH(vp, &j->params, tq)
197306196Sjkim					if (!strcmp(vp->name, v->name))
198280304Sjkim						break;
199280304Sjkim				if (!vp) {
20055714Skris					jail_warnx(j,
201280304Sjkim					    "%s: variable \"%s\" not found",
202280304Sjkim					    p->name, v->name);
203280304Sjkim				bad_var:
20455714Skris					j->flags |= JF_FAILED;
205280304Sjkim					TAILQ_FOREACH(vp, &j->params, tq)
206280304Sjkim						if (vp->gen == pgen)
207280304Sjkim							vp->flags |= PF_BAD;
208280304Sjkim					goto free_var;
209280304Sjkim				}
210280304Sjkim				if (vp->flags & PF_BAD)
211280304Sjkim					goto bad_var;
21255714Skris				if (vp->gen == pgen) {
213280304Sjkim					jail_warnx(j, "%s: variable loop",
214280304Sjkim					    v->name);
215280304Sjkim					goto bad_var;
21655714Skris				}
217280304Sjkim				TAILQ_FOREACH(vs, &vp->val, tq)
218280304Sjkim					if (!STAILQ_EMPTY(&vs->vars)) {
219280304Sjkim						vp->gen = pgen;
220280304Sjkim						TAILQ_REMOVE(&j->params, vp,
221280304Sjkim						    tq);
222280304Sjkim						TAILQ_INSERT_BEFORE(p, vp, tq);
223306196Sjkim						p = vp;
224306196Sjkim						goto find_vars;
225280304Sjkim					}
226280304Sjkim				vs = TAILQ_FIRST(&vp->val);
227280304Sjkim				if (TAILQ_NEXT(vs, tq) != NULL &&
228280304Sjkim				    (s->s[0] != '\0' ||
229280304Sjkim				     STAILQ_NEXT(v, tq))) {
230280304Sjkim					jail_warnx(j, "%s: array cannot be "
231280304Sjkim					    "substituted inline",
232280304Sjkim					    p->name);
233280304Sjkim					goto bad_var;
234280304Sjkim				}
235280304Sjkim				s->s = erealloc(s->s, s->len + vs->len + 1);
236280304Sjkim				memmove(s->s + v->pos + varoff + vs->len,
237280304Sjkim				    s->s + v->pos + varoff,
238280304Sjkim				    s->len - (v->pos + varoff) + 1);
239280304Sjkim				memcpy(s->s + v->pos + varoff, vs->s, vs->len);
24055714Skris				varoff += vs->len;
241284285Sjkim				s->len += vs->len;
242284285Sjkim				while ((vs = TAILQ_NEXT(vs, tq))) {
243280304Sjkim					ns = emalloc(sizeof(struct cfstring));
244280304Sjkim					ns->s = estrdup(vs->s);
245280304Sjkim					ns->len = vs->len;
246280304Sjkim					STAILQ_INIT(&ns->vars);
247280304Sjkim					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
24855714Skris					s = ns;
249284285Sjkim				}
250284285Sjkim			free_var:
251284285Sjkim				free(v->name);
252284285Sjkim				STAILQ_REMOVE_HEAD(&s->vars, tq);
253284285Sjkim				free(v);
254284285Sjkim			}
255284285Sjkim		    }
256284285Sjkim		}
257284285Sjkim
258284285Sjkim		/* Free the jail's original parameter list and any variables. */
259284285Sjkim		while ((p = TAILQ_FIRST(&opp)))
260284285Sjkim			free_param(&opp, p);
261284285Sjkim		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
262284285Sjkim			if (p->flags & PF_VAR)
263284285Sjkim				free_param(&j->params, p);
264284285Sjkim	}
265284285Sjkim	while ((wj = TAILQ_FIRST(&wild))) {
266284285Sjkim		free(wj->name);
267284285Sjkim		while ((p = TAILQ_FIRST(&wj->params)))
268284285Sjkim			free_param(&wj->params, p);
269284285Sjkim		TAILQ_REMOVE(&wild, wj, tq);
270284285Sjkim	}
271284285Sjkim}
272284285Sjkim
273284285Sjkim/*
274284285Sjkim * Create a new jail record.
275284285Sjkim */
276284285Sjkimstruct cfjail *
277284285Sjkimadd_jail(void)
278284285Sjkim{
279284285Sjkim	struct cfjail *j;
280284285Sjkim
281284285Sjkim	j = emalloc(sizeof(struct cfjail));
282284285Sjkim	memset(j, 0, sizeof(struct cfjail));
283284285Sjkim	TAILQ_INIT(&j->params);
284280304Sjkim	STAILQ_INIT(&j->dep[DEP_FROM]);
285280304Sjkim	STAILQ_INIT(&j->dep[DEP_TO]);
286284285Sjkim	j->queue = &cfjails;
287280304Sjkim	TAILQ_INSERT_TAIL(&cfjails, j, tq);
288284285Sjkim	return j;
289284285Sjkim}
290284285Sjkim
291284285Sjkim/*
292284285Sjkim * Add a parameter to a jail.
293280304Sjkim */
294280304Sjkimvoid
295284285Sjkimadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
296284285Sjkim    const char *value)
297284285Sjkim{
298284285Sjkim	struct cfstrings nss;
299284285Sjkim	struct cfparam *dp, *np;
300284285Sjkim	struct cfstring *s, *ns;
301284285Sjkim	struct cfvar *v, *nv;
302284285Sjkim	const char *name;
303284285Sjkim	char *cs, *tname;
304306196Sjkim	unsigned flags;
305306196Sjkim
306306196Sjkim	if (j == NULL) {
307306196Sjkim		/* Create a single anonymous jail if one doesn't yet exist. */
308284285Sjkim		j = TAILQ_LAST(&cfjails, cfjails);
309284285Sjkim		if (j == NULL)
310284285Sjkim			j = add_jail();
311284285Sjkim	}
312284285Sjkim	TAILQ_INIT(&nss);
313284285Sjkim	if (p != NULL) {
314306196Sjkim		name = p->name;
315306196Sjkim		flags = p->flags;
316306196Sjkim		/*
317284285Sjkim		 * Make a copy of the parameter's string list,
318284285Sjkim		 * which may be freed if it's overridden later.
319284285Sjkim		 */
32068651Skris		TAILQ_FOREACH(s, &p->val, tq) {
321284285Sjkim			ns = emalloc(sizeof(struct cfstring));
322284285Sjkim			ns->s = estrdup(s->s);
323284285Sjkim			ns->len = s->len;
324284285Sjkim			STAILQ_INIT(&ns->vars);
325284285Sjkim			STAILQ_FOREACH(v, &s->vars, tq) {
326284285Sjkim				nv = emalloc(sizeof(struct cfvar));
327285330Sjkim				nv->name = strdup(v->name);
328284285Sjkim				nv->pos = v->pos;
329284285Sjkim				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
330284285Sjkim			}
331284285Sjkim			TAILQ_INSERT_TAIL(&nss, ns, tq);
332306196Sjkim		}
333306196Sjkim	} else {
334284285Sjkim		flags = PF_APPEND;
335306196Sjkim		if (ipnum != IP__NULL) {
336284285Sjkim			name = intparams[ipnum].name;
337284285Sjkim			flags |= intparams[ipnum].flags;
338284285Sjkim		} else if ((cs = strchr(value, '='))) {
339284285Sjkim			tname = alloca(cs - value + 1);
340284285Sjkim			strlcpy(tname, value, cs - value + 1);
341284285Sjkim			name = tname;
34255714Skris			value = cs + 1;
343284285Sjkim		} else {
344284285Sjkim			name = value;
345284285Sjkim			value = NULL;
346284285Sjkim		}
347284285Sjkim		if (value != NULL) {
348284285Sjkim			ns = emalloc(sizeof(struct cfstring));
349284285Sjkim			ns->s = estrdup(value);
350284285Sjkim			ns->len = strlen(value);
351284285Sjkim			STAILQ_INIT(&ns->vars);
352285330Sjkim			TAILQ_INSERT_TAIL(&nss, ns, tq);
353284285Sjkim		}
354284285Sjkim	}
355284285Sjkim
356284285Sjkim	/* See if this parameter has already been added. */
357280304Sjkim	if (ipnum != IP__NULL)
358284285Sjkim		dp = j->intparams[ipnum];
35968651Skris	else
360280304Sjkim		TAILQ_FOREACH(dp, &j->params, tq)
361280304Sjkim			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
362280304Sjkim				break;
363280304Sjkim	if (dp != NULL) {
364280304Sjkim		/* Found it - append or replace. */
365280304Sjkim		if (strcmp(dp->name, name)) {
366280304Sjkim			free(dp->name);
367280304Sjkim			dp->name = estrdup(name);
368280304Sjkim		}
36968651Skris		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
370280304Sjkim			free_param_strings(dp);
371280304Sjkim		TAILQ_CONCAT(&dp->val, &nss, tq);
372280304Sjkim		dp->flags |= flags;
373280304Sjkim	} else {
374280304Sjkim		/* Not found - add it. */
375280304Sjkim		np = emalloc(sizeof(struct cfparam));
376280304Sjkim		np->name = estrdup(name);
37755714Skris		TAILQ_INIT(&np->val);
378280304Sjkim		TAILQ_CONCAT(&np->val, &nss, tq);
379280304Sjkim		np->flags = flags;
380280304Sjkim		np->gen = 0;
381280304Sjkim		TAILQ_INSERT_TAIL(&j->params, np, tq);
382280304Sjkim		if (ipnum != IP__NULL)
383280304Sjkim			j->intparams[ipnum] = np;
38455714Skris		else
385280304Sjkim			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
386280304Sjkim				if (!(intparams[ipnum].flags & PF_CONV) &&
38755714Skris				    equalopts(name, intparams[ipnum].name)) {
388280304Sjkim					j->intparams[ipnum] = np;
389280304Sjkim					np->flags |= intparams[ipnum].flags;
39059191Skris					break;
391280304Sjkim				}
39259191Skris	}
393280304Sjkim}
394238405Sjkim
395280304Sjkim/*
396280304Sjkim * Return if a boolean parameter exists and is true.
397238405Sjkim */
398280304Sjkimint
39959191Skrisbool_param(const struct cfparam *p)
400280304Sjkim{
401280304Sjkim	const char *cs;
40259191Skris
403280304Sjkim	if (p == NULL)
404280304Sjkim		return 0;
40559191Skris	cs = strrchr(p->name, '.');
406280304Sjkim	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
407280304Sjkim	    (TAILQ_EMPTY(&p->val) ||
40855714Skris	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
409280304Sjkim	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
410280304Sjkim}
411280304Sjkim
412280304Sjkim/*
413109998Smarkm * Set an integer if a parameter if it exists.
414280304Sjkim */
415280304Sjkimint
416280304Sjkimint_param(const struct cfparam *p, int *ip)
417109998Smarkm{
418280304Sjkim	if (p == NULL || TAILQ_EMPTY(&p->val))
419280304Sjkim		return 0;
420280304Sjkim	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
421280304Sjkim	return 1;
422280304Sjkim}
423280304Sjkim
424280304Sjkim/*
425160814Ssimon * Return the string value of a scalar parameter if it exists.
426167612Ssimon */
427280304Sjkimconst char *
428280304Sjkimstring_param(const struct cfparam *p)
429280304Sjkim{
430280304Sjkim	return (p && !TAILQ_EMPTY(&p->val)
431280304Sjkim	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
432280304Sjkim}
433280304Sjkim
434167612Ssimon/*
435167612Ssimon * Check syntax and values of internal parameters.  Set some internal
436280304Sjkim * parameters based on the values of others.
437280304Sjkim */
438280304Sjkimint
439280304Sjkimcheck_intparams(struct cfjail *j)
440280304Sjkim{
441280304Sjkim	struct cfparam *p;
442280304Sjkim	struct cfstring *s;
443280304Sjkim	FILE *f;
444280304Sjkim	const char *val;
445280304Sjkim	char *cs, *ep, *ln;
446280304Sjkim	size_t lnlen;
447280304Sjkim	int error;
448280304Sjkim#if defined(INET) || defined(INET6)
449306196Sjkim	struct addrinfo hints;
450306196Sjkim	struct addrinfo *ai0, *ai;
451306196Sjkim	const char *hostname;
452306196Sjkim	int gicode, defif, prefix;
453280304Sjkim#endif
454280304Sjkim#ifdef INET
45555714Skris	struct in_addr addr4;
456280304Sjkim	int ip4ok;
457280304Sjkim	char avalue4[INET_ADDRSTRLEN];
45868651Skris#endif
45968651Skris#ifdef INET6
46068651Skris	struct in6_addr addr6;
46168651Skris	int ip6ok;
462280304Sjkim	char avalue6[INET6_ADDRSTRLEN];
463280304Sjkim#endif
464280304Sjkim
465280304Sjkim	error = 0;
466280304Sjkim	/* Check format of boolan and integer values. */
467280304Sjkim	TAILQ_FOREACH(p, &j->params, tq) {
468280304Sjkim		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
469280304Sjkim			val = TAILQ_LAST(&p->val, cfstrings)->s;
47068651Skris			if (p->flags & PF_BOOL) {
47168651Skris				if (strcasecmp(val, "false") &&
47268651Skris				    strcasecmp(val, "true") &&
47368651Skris				    ((void)strtol(val, &ep, 10), *ep)) {
47468651Skris					jail_warnx(j,
47568651Skris					    "%s: unknown boolean value \"%s\"",
476280304Sjkim					    p->name, val);
477280304Sjkim					error = -1;
478280304Sjkim				}
479280304Sjkim			} else {
480280304Sjkim				(void)strtol(val, &ep, 10);
481280304Sjkim				if (ep == val || *ep) {
482280304Sjkim					jail_warnx(j,
48368651Skris					    "%s: non-integer value \"%s\"",
484280304Sjkim					    p->name, val);
485280304Sjkim					error = -1;
486280304Sjkim				}
487280304Sjkim			}
488280304Sjkim		}
48968651Skris	}
49068651Skris
49168651Skris#if defined(INET) || defined(INET6)
49268651Skris	/*
49368651Skris	 * The ip_hostname parameter looks up the hostname, and adds parameters
49468651Skris	 * for any IP addresses it finds.
495280304Sjkim	 */
496280304Sjkim	if (((j->flags & JF_OP_MASK) != JF_STOP ||
497280304Sjkim	    j->intparams[IP_INTERFACE] != NULL) &&
498280304Sjkim	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
499280304Sjkim	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
500280304Sjkim		j->intparams[IP_IP_HOSTNAME] = NULL;
50168651Skris		/*
50268651Skris		 * Silently ignore unsupported address families from
503280304Sjkim		 * DNS lookups.
504280304Sjkim		 */
505280304Sjkim#ifdef INET
50659191Skris		ip4ok = feature_present("inet");
50759191Skris#endif
508160814Ssimon#ifdef INET6
50959191Skris		ip6ok = feature_present("inet6");
510109998Smarkm#endif
511280304Sjkim		if (
51259191Skris#if defined(INET) && defined(INET6)
513280304Sjkim		    ip4ok || ip6ok
514280304Sjkim#elif defined(INET)
515280304Sjkim		    ip4ok
516280304Sjkim#elif defined(INET6)
517280304Sjkim		    ip6ok
518280304Sjkim#endif
519280304Sjkim			 ) {
520160814Ssimon			/* Look up the hostname (or get the address) */
521280304Sjkim			memset(&hints, 0, sizeof(hints));
522280304Sjkim			hints.ai_socktype = SOCK_STREAM;
523280304Sjkim			hints.ai_family =
524280304Sjkim#if defined(INET) && defined(INET6)
525280304Sjkim			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
526280304Sjkim#elif defined(INET)
527280304Sjkim			    PF_INET;
528280304Sjkim#elif defined(INET6)
529280304Sjkim			    PF_INET6;
530280304Sjkim#endif
531160814Ssimon			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
532280304Sjkim			if (gicode != 0) {
533280304Sjkim				jail_warnx(j, "host.hostname %s: %s", hostname,
534280304Sjkim				    gai_strerror(gicode));
535280304Sjkim				error = -1;
536280304Sjkim			} else {
537280304Sjkim				/*
538280304Sjkim				 * Convert the addresses to ASCII so jailparam
539280304Sjkim				 * can convert them back.  Errors are not
540280304Sjkim				 * expected here.
541280304Sjkim				 */
542280304Sjkim				for (ai = ai0; ai; ai = ai->ai_next)
543280304Sjkim					switch (ai->ai_family) {
544280304Sjkim#ifdef INET
545280304Sjkim					case AF_INET:
546280304Sjkim						memcpy(&addr4,
547160814Ssimon						    &((struct sockaddr_in *)
548280304Sjkim						    (void *)ai->ai_addr)->
549280304Sjkim						    sin_addr, sizeof(addr4));
550280304Sjkim						if (inet_ntop(AF_INET,
551280304Sjkim						    &addr4, avalue4,
552280304Sjkim						    INET_ADDRSTRLEN) == NULL)
553280304Sjkim							err(1, "inet_ntop");
554280304Sjkim						add_param(j, NULL, KP_IP4_ADDR,
555280304Sjkim						    avalue4);
556280304Sjkim						break;
557280304Sjkim#endif
558280304Sjkim#ifdef INET6
559280304Sjkim					case AF_INET6:
560280304Sjkim						memcpy(&addr6,
561280304Sjkim						    &((struct sockaddr_in6 *)
562280304Sjkim						    (void *)ai->ai_addr)->
563280304Sjkim						    sin6_addr, sizeof(addr6));
564280304Sjkim						if (inet_ntop(AF_INET6,
565280304Sjkim						    &addr6, avalue6,
566280304Sjkim						    INET6_ADDRSTRLEN) == NULL)
567280304Sjkim							err(1, "inet_ntop");
568280304Sjkim						add_param(j, NULL, KP_IP6_ADDR,
569280304Sjkim						    avalue6);
570280304Sjkim						break;
571280304Sjkim#endif
572280304Sjkim					}
573280304Sjkim				freeaddrinfo(ai0);
574280304Sjkim			}
575280304Sjkim		}
576280304Sjkim	}
577280304Sjkim
578280304Sjkim	/*
579280304Sjkim	 * IP addresses may include an interface to set that address on,
580280304Sjkim	 * a netmask/suffix for that address and options for ifconfig.
581280304Sjkim	 * These are copied to an internal command parameter and then stripped
582280304Sjkim	 * so they won't be passed on to jailparam_set.
583280304Sjkim	 */
584280304Sjkim	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
585280304Sjkim#ifdef INET
586280304Sjkim	if (j->intparams[KP_IP4_ADDR] != NULL) {
587280304Sjkim		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
588280304Sjkim			cs = strchr(s->s, '|');
589280304Sjkim			if (cs || defif)
590280304Sjkim				add_param(j, NULL, IP__IP4_IFADDR, s->s);
591280304Sjkim			if (cs) {
592280304Sjkim				strcpy(s->s, cs + 1);
593280304Sjkim				s->len -= cs + 1 - s->s;
594280304Sjkim			}
595280304Sjkim			if ((cs = strchr(s->s, '/'))) {
596280304Sjkim				prefix = strtol(cs + 1, &ep, 10);
597280304Sjkim				if (*ep == '.'
598280304Sjkim				    ? inet_pton(AF_INET, cs + 1, &addr4) != 1
599280304Sjkim				    : *ep || prefix < 0 || prefix > 32) {
600280304Sjkim					jail_warnx(j,
601280304Sjkim					    "ip4.addr: bad netmask \"%s\"", cs);
602280304Sjkim					error = -1;
603280304Sjkim				}
604280304Sjkim				*cs = '\0';
605280304Sjkim				s->len = cs - s->s;
606280304Sjkim			}
607280304Sjkim			if ((cs = strchr(s->s, ' ')) != NULL) {
608280304Sjkim				*cs = '\0';
609280304Sjkim				s->len = cs - s->s;
610280304Sjkim			}
611280304Sjkim		}
612280304Sjkim	}
613280304Sjkim#endif
614280304Sjkim#ifdef INET6
615280304Sjkim	if (j->intparams[KP_IP6_ADDR] != NULL) {
616280304Sjkim		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
617280304Sjkim			cs = strchr(s->s, '|');
618280304Sjkim			if (cs || defif)
619280304Sjkim				add_param(j, NULL, IP__IP6_IFADDR, s->s);
620280304Sjkim			if (cs) {
621280304Sjkim				strcpy(s->s, cs + 1);
622280304Sjkim				s->len -= cs + 1 - s->s;
623280304Sjkim			}
624280304Sjkim			if ((cs = strchr(s->s, '/'))) {
625280304Sjkim				prefix = strtol(cs + 1, &ep, 10);
626280304Sjkim				if (*ep || prefix < 0 || prefix > 128) {
627280304Sjkim					jail_warnx(j,
628280304Sjkim					    "ip6.addr: bad prefixlen \"%s\"",
629280304Sjkim					    cs);
630280304Sjkim					error = -1;
631280304Sjkim				}
632280304Sjkim				*cs = '\0';
633280304Sjkim				s->len = cs - s->s;
634280304Sjkim			}
635280304Sjkim			if ((cs = strchr(s->s, ' ')) != NULL) {
636306196Sjkim				*cs = '\0';
637306196Sjkim				s->len = cs - s->s;
638306196Sjkim			}
639306196Sjkim		}
640306196Sjkim	}
641306196Sjkim#endif
642306196Sjkim#endif
643306196Sjkim
644306196Sjkim	/*
645306196Sjkim	 * Read mount.fstab file(s), and treat each line as its own mount
646306196Sjkim	 * parameter.
647306196Sjkim	 */
648306196Sjkim	if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
649306196Sjkim		TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
650306196Sjkim			if (s->len == 0)
651306196Sjkim				continue;
652306196Sjkim			f = fopen(s->s, "r");
653306196Sjkim			if (f == NULL) {
654306196Sjkim				jail_warnx(j, "mount.fstab: %s: %s",
655306196Sjkim				    s->s, strerror(errno));
656306196Sjkim				error = -1;
657280304Sjkim				continue;
658280304Sjkim			}
659280304Sjkim			while ((ln = fgetln(f, &lnlen))) {
660280304Sjkim				if ((cs = memchr(ln, '#', lnlen - 1)))
661280304Sjkim					lnlen = cs - ln + 1;
662280304Sjkim				if (ln[lnlen - 1] == '\n' ||
663280304Sjkim				    ln[lnlen - 1] == '#')
66468651Skris					ln[lnlen - 1] = '\0';
665280304Sjkim				else {
66659191Skris					cs = alloca(lnlen + 1);
66759191Skris					strlcpy(cs, ln, lnlen + 1);
66859191Skris					ln = cs;
669238405Sjkim				}
670280304Sjkim				add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
671280304Sjkim			}
672280304Sjkim			fclose(f);
673280304Sjkim		}
674280304Sjkim	}
675280304Sjkim	if (error)
676280304Sjkim		failed(j);
677280304Sjkim	return error;
678280304Sjkim}
679306196Sjkim
680280304Sjkim/*
681306196Sjkim * Import parameters into libjail's binary jailparam format.
682306196Sjkim */
683306196Sjkimint
684306196Sjkimimport_params(struct cfjail *j)
685306196Sjkim{
686306196Sjkim	struct cfparam *p;
687306196Sjkim	struct cfstring *s, *ts;
688306196Sjkim	struct jailparam *jp;
689306196Sjkim	char *value, *cs;
690306196Sjkim	size_t vallen;
691306196Sjkim	int error;
692306196Sjkim
693306196Sjkim	error = 0;
694306196Sjkim	j->njp = 0;
695306196Sjkim	TAILQ_FOREACH(p, &j->params, tq)
696306196Sjkim		if (!(p->flags & PF_INTERNAL))
697306196Sjkim			j->njp++;
698306196Sjkim	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
699306196Sjkim	TAILQ_FOREACH(p, &j->params, tq) {
700306196Sjkim		if (p->flags & PF_INTERNAL)
701306196Sjkim			continue;
702306196Sjkim		if (jailparam_init(jp, p->name) < 0) {
703306196Sjkim			error = -1;
704306196Sjkim			jail_warnx(j, "%s", jail_errmsg);
705306196Sjkim			jp++;
706306196Sjkim			continue;
707306196Sjkim		}
708306196Sjkim		if (TAILQ_EMPTY(&p->val))
709306196Sjkim			value = NULL;
710306196Sjkim		else if (!jp->jp_elemlen ||
711306196Sjkim			 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
712306196Sjkim			/*
713306196Sjkim			 * Scalar parameters silently discard multiple (array)
714306196Sjkim			 * values, keeping only the last value added.  This
715306196Sjkim			 * lets values added from the command line append to
716306196Sjkim			 * arrays wthout pre-checking the type.
717306196Sjkim			 */
718306196Sjkim			value = TAILQ_LAST(&p->val, cfstrings)->s;
719306196Sjkim		} else {
720306196Sjkim			/*
721306196Sjkim			 * Convert arrays into comma-separated strings, which
722306196Sjkim			 * jailparam_import will then convert back into arrays.
723306196Sjkim			 */
724306196Sjkim			vallen = 0;
725306196Sjkim			TAILQ_FOREACH(s, &p->val, tq)
726306196Sjkim				vallen += s->len + 1;
727306196Sjkim			value = alloca(vallen);
728306196Sjkim			cs = value;
729306196Sjkim			TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
730306196Sjkim				memcpy(cs, s->s, s->len);
731306196Sjkim				cs += s->len + 1;
732306196Sjkim				cs[-1] = ',';
733306196Sjkim			}
734306196Sjkim			value[vallen - 1] = '\0';
735306196Sjkim		}
736306196Sjkim		if (jailparam_import(jp, value) < 0) {
737306196Sjkim			error = -1;
738306196Sjkim			jail_warnx(j, "%s", jail_errmsg);
739306196Sjkim		}
740306196Sjkim		jp++;
741306196Sjkim	}
742306196Sjkim	if (error) {
743306196Sjkim		jailparam_free(j->jp, j->njp);
744306196Sjkim		free(j->jp);
745306196Sjkim		j->jp = NULL;
746306196Sjkim		failed(j);
747306196Sjkim	}
748306196Sjkim	return error;
749306196Sjkim}
750306196Sjkim
751306196Sjkim/*
752306196Sjkim * Check if options are equal (with or without the "no" prefix).
753306196Sjkim */
754306196Sjkimint
755280304Sjkimequalopts(const char *opt1, const char *opt2)
756280304Sjkim{
757280304Sjkim	char *p;
758280304Sjkim
759280304Sjkim	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
760280304Sjkim	if (strcmp(opt1, opt2) == 0)
761280304Sjkim		return (1);
762280304Sjkim	/* "noopt" vs. "opt" */
763280304Sjkim	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
764306196Sjkim		return (1);
765306196Sjkim	/* "opt" vs. "noopt" */
766306196Sjkim	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
767306196Sjkim		return (1);
768280304Sjkim	while ((p = strchr(opt1, '.')) != NULL &&
769306196Sjkim	    !strncmp(opt1, opt2, ++p - opt1)) {
770306196Sjkim		opt2 += p - opt1;
771306196Sjkim		opt1 = p;
772280304Sjkim		/* "foo.noopt" vs. "foo.opt" */
773280304Sjkim		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
774280304Sjkim			return (1);
775280304Sjkim		/* "foo.opt" vs. "foo.noopt" */
776306196Sjkim		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
777280304Sjkim			return (1);
778280304Sjkim	}
779280304Sjkim	return (0);
780280304Sjkim}
781280304Sjkim
782280304Sjkim/*
783238405Sjkim * See if a jail name matches a wildcard.
78459191Skris */
78559191Skrisint
786109998Smarkmwild_jail_match(const char *jname, const char *wname)
787280304Sjkim{
78859191Skris	const char *jc, *jd, *wc, *wd;
789280304Sjkim
790280304Sjkim	/*
791280304Sjkim	 * A non-final "*" component in the wild name matches a single jail
792280304Sjkim	 * component, and a final "*" matches one or more jail components.
79359191Skris	 */
794280304Sjkim	for (jc = jname, wc = wname;
795280304Sjkim	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
796280304Sjkim	     jc = jd + 1, wc = wd + 1)
797280304Sjkim		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
798280304Sjkim			return 0;
799280304Sjkim	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
800280304Sjkim}
801280304Sjkim
802280304Sjkim/*
803280304Sjkim * Return if a jail name is a wildcard.
804280304Sjkim */
805280304Sjkimint
806280304Sjkimwild_jail_name(const char *wname)
80759191Skris{
80859191Skris	const char *wc;
80959191Skris
810109998Smarkm	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
811280304Sjkim		if ((wc == wname || wc[-1] == '.') &&
812280304Sjkim		    (wc[1] == '\0' || wc[1] == '.'))
813280304Sjkim			return 1;
814280304Sjkim	return 0;
815280304Sjkim}
816280304Sjkim
817280304Sjkim/*
818280304Sjkim * Free a parameter record and all its strings and variables.
819280304Sjkim */
820280304Sjkimstatic void
821280304Sjkimfree_param(struct cfparams *pp, struct cfparam *p)
822280304Sjkim{
823280304Sjkim	free(p->name);
824280304Sjkim	free_param_strings(p);
825280304Sjkim	TAILQ_REMOVE(pp, p, tq);
826280304Sjkim	free(p);
827280304Sjkim}
828280304Sjkim
829280304Sjkimstatic void
830280304Sjkimfree_param_strings(struct cfparam *p)
831109998Smarkm{
832109998Smarkm	struct cfstring *s;
833280304Sjkim	struct cfvar *v;
834280304Sjkim
835280304Sjkim	while ((s = TAILQ_FIRST(&p->val))) {
836280304Sjkim		free(s->s);
837280304Sjkim		while ((v = STAILQ_FIRST(&s->vars))) {
838280304Sjkim			free(v->name);
839280304Sjkim			STAILQ_REMOVE_HEAD(&s->vars, tq);
840280304Sjkim			free(v);
841280304Sjkim		}
842280304Sjkim		TAILQ_REMOVE(&p->val, s, tq);
843280304Sjkim		free(s);
844280304Sjkim	}
845280304Sjkim}
846280304Sjkim