1214117Sjamie/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4223190Sjamie * Copyright (c) 2011 James Gritton
5214117Sjamie * All rights reserved.
6214117Sjamie *
7214117Sjamie * Redistribution and use in source and binary forms, with or without
8214117Sjamie * modification, are permitted provided that the following conditions
9214117Sjamie * are met:
10214117Sjamie * 1. Redistributions of source code must retain the above copyright
11214117Sjamie *    notice, this list of conditions and the following disclaimer.
12214117Sjamie * 2. Redistributions in binary form must reproduce the above copyright
13214117Sjamie *    notice, this list of conditions and the following disclaimer in the
14214117Sjamie *    documentation and/or other materials provided with the distribution.
15214117Sjamie *
16214117Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17214117Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18214117Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19214117Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20214117Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21214117Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22214117Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23214117Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24214117Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25214117Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26214117Sjamie * SUCH DAMAGE.
27214117Sjamie */
28214117Sjamie
29214117Sjamie#include <sys/cdefs.h>
30214117Sjamie__FBSDID("$FreeBSD: stable/11/usr.sbin/jail/config.c 370034 2021-06-24 17:55:37Z jamie $");
31214117Sjamie
32214117Sjamie#include <sys/types.h>
33214783Sjamie#include <sys/errno.h>
34214117Sjamie#include <sys/socket.h>
35214117Sjamie#include <sys/sysctl.h>
36214117Sjamie
37214117Sjamie#include <arpa/inet.h>
38214117Sjamie#include <netinet/in.h>
39214117Sjamie
40214117Sjamie#include <err.h>
41214117Sjamie#include <netdb.h>
42214117Sjamie#include <stdio.h>
43214117Sjamie#include <stdlib.h>
44214117Sjamie#include <string.h>
45223351Sjamie#include <unistd.h>
46214117Sjamie
47214117Sjamie#include "jailp.h"
48214117Sjamie
49214117Sjamiestruct ipspec {
50214117Sjamie	const char	*name;
51214117Sjamie	unsigned	flags;
52214117Sjamie};
53214117Sjamie
54214117Sjamieextern FILE *yyin;
55214117Sjamieextern int yynerrs;
56214117Sjamie
57235789Sbaptextern int yyparse(void);
58235789Sbapt
59214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
60214117Sjamie
61214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p);
62214117Sjamiestatic void free_param_strings(struct cfparam *p);
63214117Sjamie
64214117Sjamiestatic const struct ipspec intparams[] = {
65214423Sjamie    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
66214423Sjamie    [IP_COMMAND] =		{"command",		PF_INTERNAL},
67214423Sjamie    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
68214423Sjamie    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
69214423Sjamie    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
70214423Sjamie    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
71214423Sjamie    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
72214423Sjamie    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
73214423Sjamie    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
74214423Sjamie    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
75214423Sjamie    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
76214423Sjamie    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
77214423Sjamie    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
78214423Sjamie    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
79214423Sjamie							PF_INTERNAL | PF_BOOL},
80214423Sjamie    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
81214423Sjamie    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
82223351Sjamie#if defined(INET) || defined(INET6)
83214423Sjamie    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
84214423Sjamie    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
85223351Sjamie#endif
86248854Sjamie    [IP_MOUNT] =		{"mount",		PF_INTERNAL | PF_REV},
87214423Sjamie    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
88256385Shrs    [IP_MOUNT_FDESCFS] =	{"mount.fdescfs",	PF_INTERNAL | PF_BOOL},
89278323Sjamie    [IP_MOUNT_PROCFS] =		{"mount.procfs",	PF_INTERNAL | PF_BOOL},
90214423Sjamie    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
91214423Sjamie    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
92214423Sjamie    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
93223351Sjamie#ifdef INET
94248854Sjamie    [IP__IP4_IFADDR] =		{"ip4.addr",	PF_INTERNAL | PF_CONV | PF_REV},
95223351Sjamie#endif
96214117Sjamie#ifdef INET6
97248854Sjamie    [IP__IP6_IFADDR] =		{"ip6.addr",	PF_INTERNAL | PF_CONV | PF_REV},
98214117Sjamie#endif
99248854Sjamie    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",	PF_INTERNAL | PF_CONV | PF_REV},
100223189Sjamie    [IP__OP] =			{NULL,			PF_CONV},
101214423Sjamie    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
102214423Sjamie    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
103214423Sjamie    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
104214423Sjamie    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
105214423Sjamie    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
106214423Sjamie    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
107232242Sjamie    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
108214423Sjamie    [KP_ENFORCE_STATFS] =	{"enforce_statfs",	0},
109214423Sjamie    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
110223351Sjamie#ifdef INET
111214423Sjamie    [KP_IP4_ADDR] =		{"ip4.addr",		0},
112223351Sjamie#endif
113214423Sjamie#ifdef INET6
114214423Sjamie    [KP_IP6_ADDR] =		{"ip6.addr",		0},
115214423Sjamie#endif
116285279Shrs    [KP_JID] =			{"jid",			PF_IMMUTABLE},
117285279Shrs    [KP_NAME] =			{"name",		PF_IMMUTABLE},
118214423Sjamie    [KP_PATH] =			{"path",		0},
119214423Sjamie    [KP_PERSIST] =		{"persist",		0},
120214423Sjamie    [KP_SECURELEVEL] =		{"securelevel",		0},
121214423Sjamie    [KP_VNET] =			{"vnet",		0},
122214117Sjamie};
123214117Sjamie
124214117Sjamie/*
125214117Sjamie * Parse the jail configuration file.
126214117Sjamie */
127214117Sjamievoid
128214117Sjamieload_config(void)
129214117Sjamie{
130214117Sjamie	struct cfjails wild;
131214117Sjamie	struct cfparams opp;
132214117Sjamie	struct cfjail *j, *tj, *wj;
133214117Sjamie	struct cfparam *p, *vp, *tp;
134214117Sjamie	struct cfstring *s, *vs, *ns;
135285261Shrs	struct cfvar *v, *vv;
136214117Sjamie	char *ep;
137214117Sjamie	int did_self, jseq, pgen;
138214117Sjamie
139214117Sjamie	if (!strcmp(cfname, "-")) {
140214117Sjamie		cfname = "STDIN";
141214117Sjamie		yyin = stdin;
142214117Sjamie	} else {
143214117Sjamie		yyin = fopen(cfname, "r");
144214117Sjamie		if (!yyin)
145214117Sjamie			err(1, "%s", cfname);
146214117Sjamie	}
147214117Sjamie	if (yyparse() || yynerrs)
148214117Sjamie		exit(1);
149214117Sjamie
150214117Sjamie	/* Separate the wildcard jails out from the actual jails. */
151214117Sjamie	jseq = 0;
152214117Sjamie	TAILQ_INIT(&wild);
153214117Sjamie	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
154214117Sjamie		j->seq = ++jseq;
155214117Sjamie		if (wild_jail_name(j->name))
156214117Sjamie			requeue(j, &wild);
157214117Sjamie	}
158214117Sjamie
159214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq) {
160214117Sjamie		/* Set aside the jail's parameters. */
161214117Sjamie		TAILQ_INIT(&opp);
162214117Sjamie		TAILQ_CONCAT(&opp, &j->params, tq);
163214117Sjamie		/*
164214117Sjamie		 * The jail name implies its "name" or "jid" parameter,
165214117Sjamie		 * though they may also be explicitly set later on.
166214117Sjamie		 */
167214117Sjamie		add_param(j, NULL,
168214423Sjamie		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
169214117Sjamie		    j->name);
170214117Sjamie		/*
171214117Sjamie		 * Collect parameters for the jail, global parameters/variables,
172214117Sjamie		 * and any matching wildcard jails.
173214117Sjamie		 */
174214117Sjamie		did_self = 0;
175214117Sjamie		TAILQ_FOREACH(wj, &wild, tq) {
176214117Sjamie			if (j->seq < wj->seq && !did_self) {
177214117Sjamie				TAILQ_FOREACH(p, &opp, tq)
178214423Sjamie					add_param(j, p, 0, NULL);
179214117Sjamie				did_self = 1;
180214117Sjamie			}
181214117Sjamie			if (wild_jail_match(j->name, wj->name))
182214117Sjamie				TAILQ_FOREACH(p, &wj->params, tq)
183214423Sjamie					add_param(j, p, 0, NULL);
184214117Sjamie		}
185214117Sjamie		if (!did_self)
186214117Sjamie			TAILQ_FOREACH(p, &opp, tq)
187214423Sjamie				add_param(j, p, 0, NULL);
188214117Sjamie
189214117Sjamie		/* Resolve any variable substitutions. */
190214117Sjamie		pgen = 0;
191214117Sjamie		TAILQ_FOREACH(p, &j->params, tq) {
192214117Sjamie		    p->gen = ++pgen;
193214117Sjamie		find_vars:
194223188Sjamie		    TAILQ_FOREACH(s, &p->val, tq) {
195214117Sjamie			while ((v = STAILQ_FIRST(&s->vars))) {
196214117Sjamie				TAILQ_FOREACH(vp, &j->params, tq)
197214117Sjamie					if (!strcmp(vp->name, v->name))
198214117Sjamie						break;
199370034Sjamie				if (!vp || TAILQ_EMPTY(&vp->val)) {
200214117Sjamie					jail_warnx(j,
201214117Sjamie					    "%s: variable \"%s\" not found",
202214117Sjamie					    p->name, v->name);
203214117Sjamie				bad_var:
204214117Sjamie					j->flags |= JF_FAILED;
205214117Sjamie					TAILQ_FOREACH(vp, &j->params, tq)
206214117Sjamie						if (vp->gen == pgen)
207214117Sjamie							vp->flags |= PF_BAD;
208214117Sjamie					goto free_var;
209214117Sjamie				}
210214117Sjamie				if (vp->flags & PF_BAD)
211214117Sjamie					goto bad_var;
212214117Sjamie				if (vp->gen == pgen) {
213214117Sjamie					jail_warnx(j, "%s: variable loop",
214214117Sjamie					    v->name);
215214117Sjamie					goto bad_var;
216214117Sjamie				}
217223188Sjamie				TAILQ_FOREACH(vs, &vp->val, tq)
218214117Sjamie					if (!STAILQ_EMPTY(&vs->vars)) {
219214117Sjamie						vp->gen = pgen;
220214117Sjamie						TAILQ_REMOVE(&j->params, vp,
221214117Sjamie						    tq);
222214117Sjamie						TAILQ_INSERT_BEFORE(p, vp, tq);
223214117Sjamie						p = vp;
224214117Sjamie						goto find_vars;
225214117Sjamie					}
226223188Sjamie				vs = TAILQ_FIRST(&vp->val);
227223188Sjamie				if (TAILQ_NEXT(vs, tq) != NULL &&
228214117Sjamie				    (s->s[0] != '\0' ||
229214117Sjamie				     STAILQ_NEXT(v, tq))) {
230214117Sjamie					jail_warnx(j, "%s: array cannot be "
231214117Sjamie					    "substituted inline",
232214117Sjamie					    p->name);
233214117Sjamie					goto bad_var;
234214117Sjamie				}
235214117Sjamie				s->s = erealloc(s->s, s->len + vs->len + 1);
236285261Shrs				memmove(s->s + v->pos + vs->len,
237285261Shrs				    s->s + v->pos,
238285261Shrs				    s->len - v->pos + 1);
239285261Shrs				memcpy(s->s + v->pos, vs->s, vs->len);
240285261Shrs				vv = v;
241285261Shrs				while ((vv = STAILQ_NEXT(vv, tq)))
242285261Shrs					vv->pos += vs->len;
243214117Sjamie				s->len += vs->len;
244223188Sjamie				while ((vs = TAILQ_NEXT(vs, tq))) {
245214117Sjamie					ns = emalloc(sizeof(struct cfstring));
246214117Sjamie					ns->s = estrdup(vs->s);
247214117Sjamie					ns->len = vs->len;
248214117Sjamie					STAILQ_INIT(&ns->vars);
249223188Sjamie					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
250214117Sjamie					s = ns;
251214117Sjamie				}
252214117Sjamie			free_var:
253214117Sjamie				free(v->name);
254214117Sjamie				STAILQ_REMOVE_HEAD(&s->vars, tq);
255214117Sjamie				free(v);
256214117Sjamie			}
257214117Sjamie		    }
258214117Sjamie		}
259214117Sjamie
260214117Sjamie		/* Free the jail's original parameter list and any variables. */
261214117Sjamie		while ((p = TAILQ_FIRST(&opp)))
262214117Sjamie			free_param(&opp, p);
263214117Sjamie		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
264214117Sjamie			if (p->flags & PF_VAR)
265214117Sjamie				free_param(&j->params, p);
266214117Sjamie	}
267214117Sjamie	while ((wj = TAILQ_FIRST(&wild))) {
268214117Sjamie		free(wj->name);
269214117Sjamie		while ((p = TAILQ_FIRST(&wj->params)))
270214117Sjamie			free_param(&wj->params, p);
271214117Sjamie		TAILQ_REMOVE(&wild, wj, tq);
272214117Sjamie	}
273214117Sjamie}
274214117Sjamie
275214117Sjamie/*
276214117Sjamie * Create a new jail record.
277214117Sjamie */
278214117Sjamiestruct cfjail *
279214117Sjamieadd_jail(void)
280214117Sjamie{
281214117Sjamie	struct cfjail *j;
282214117Sjamie
283214117Sjamie	j = emalloc(sizeof(struct cfjail));
284214117Sjamie	memset(j, 0, sizeof(struct cfjail));
285214117Sjamie	TAILQ_INIT(&j->params);
286214117Sjamie	STAILQ_INIT(&j->dep[DEP_FROM]);
287214117Sjamie	STAILQ_INIT(&j->dep[DEP_TO]);
288214117Sjamie	j->queue = &cfjails;
289214117Sjamie	TAILQ_INSERT_TAIL(&cfjails, j, tq);
290214117Sjamie	return j;
291214117Sjamie}
292214117Sjamie
293214117Sjamie/*
294214117Sjamie * Add a parameter to a jail.
295214117Sjamie */
296214117Sjamievoid
297214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
298214117Sjamie    const char *value)
299214117Sjamie{
300214117Sjamie	struct cfstrings nss;
301214117Sjamie	struct cfparam *dp, *np;
302214117Sjamie	struct cfstring *s, *ns;
303214117Sjamie	struct cfvar *v, *nv;
304214423Sjamie	const char *name;
305214423Sjamie	char *cs, *tname;
306214117Sjamie	unsigned flags;
307214117Sjamie
308214117Sjamie	if (j == NULL) {
309214117Sjamie		/* Create a single anonymous jail if one doesn't yet exist. */
310214117Sjamie		j = TAILQ_LAST(&cfjails, cfjails);
311214117Sjamie		if (j == NULL)
312214117Sjamie			j = add_jail();
313214117Sjamie	}
314223188Sjamie	TAILQ_INIT(&nss);
315214117Sjamie	if (p != NULL) {
316214117Sjamie		name = p->name;
317214117Sjamie		flags = p->flags;
318214117Sjamie		/*
319214117Sjamie		 * Make a copy of the parameter's string list,
320214117Sjamie		 * which may be freed if it's overridden later.
321214117Sjamie		 */
322223188Sjamie		TAILQ_FOREACH(s, &p->val, tq) {
323214117Sjamie			ns = emalloc(sizeof(struct cfstring));
324214117Sjamie			ns->s = estrdup(s->s);
325214117Sjamie			ns->len = s->len;
326214117Sjamie			STAILQ_INIT(&ns->vars);
327214117Sjamie			STAILQ_FOREACH(v, &s->vars, tq) {
328214117Sjamie				nv = emalloc(sizeof(struct cfvar));
329214117Sjamie				nv->name = strdup(v->name);
330214117Sjamie				nv->pos = v->pos;
331214117Sjamie				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
332214117Sjamie			}
333223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
334214117Sjamie		}
335214117Sjamie	} else {
336214117Sjamie		flags = PF_APPEND;
337234988Sjamie		if (ipnum != IP__NULL) {
338214423Sjamie			name = intparams[ipnum].name;
339214423Sjamie			flags |= intparams[ipnum].flags;
340214423Sjamie		} else if ((cs = strchr(value, '='))) {
341214423Sjamie			tname = alloca(cs - value + 1);
342214423Sjamie			strlcpy(tname, value, cs - value + 1);
343214423Sjamie			name = tname;
344214423Sjamie			value = cs + 1;
345214423Sjamie		} else {
346214423Sjamie			name = value;
347214423Sjamie			value = NULL;
348214423Sjamie		}
349214117Sjamie		if (value != NULL) {
350214117Sjamie			ns = emalloc(sizeof(struct cfstring));
351214117Sjamie			ns->s = estrdup(value);
352214117Sjamie			ns->len = strlen(value);
353214117Sjamie			STAILQ_INIT(&ns->vars);
354223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
355214117Sjamie		}
356214117Sjamie	}
357214117Sjamie
358214117Sjamie	/* See if this parameter has already been added. */
359234988Sjamie	if (ipnum != IP__NULL)
360214423Sjamie		dp = j->intparams[ipnum];
361214423Sjamie	else
362214423Sjamie		TAILQ_FOREACH(dp, &j->params, tq)
363214423Sjamie			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
364214423Sjamie				break;
365214423Sjamie	if (dp != NULL) {
366214423Sjamie		/* Found it - append or replace. */
367285279Shrs		if (dp->flags & PF_IMMUTABLE) {
368285279Shrs			jail_warnx(j, "cannot redefine variable \"%s\".",
369285279Shrs			    dp->name);
370285279Shrs			return;
371285279Shrs		}
372214423Sjamie		if (strcmp(dp->name, name)) {
373214423Sjamie			free(dp->name);
374214423Sjamie			dp->name = estrdup(name);
375214117Sjamie		}
376223188Sjamie		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
377214423Sjamie			free_param_strings(dp);
378223188Sjamie		TAILQ_CONCAT(&dp->val, &nss, tq);
379214423Sjamie		dp->flags |= flags;
380214423Sjamie	} else {
381214117Sjamie		/* Not found - add it. */
382214117Sjamie		np = emalloc(sizeof(struct cfparam));
383214117Sjamie		np->name = estrdup(name);
384223188Sjamie		TAILQ_INIT(&np->val);
385223188Sjamie		TAILQ_CONCAT(&np->val, &nss, tq);
386214117Sjamie		np->flags = flags;
387214117Sjamie		np->gen = 0;
388214117Sjamie		TAILQ_INSERT_TAIL(&j->params, np, tq);
389234988Sjamie		if (ipnum != IP__NULL)
390214423Sjamie			j->intparams[ipnum] = np;
391214423Sjamie		else
392234988Sjamie			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
393214423Sjamie				if (!(intparams[ipnum].flags & PF_CONV) &&
394214423Sjamie				    equalopts(name, intparams[ipnum].name)) {
395214423Sjamie					j->intparams[ipnum] = np;
396214423Sjamie					np->flags |= intparams[ipnum].flags;
397214423Sjamie					break;
398214423Sjamie				}
399214117Sjamie	}
400214117Sjamie}
401214117Sjamie
402214117Sjamie/*
403214117Sjamie * Return if a boolean parameter exists and is true.
404214117Sjamie */
405214117Sjamieint
406214117Sjamiebool_param(const struct cfparam *p)
407214117Sjamie{
408214117Sjamie	const char *cs;
409214117Sjamie
410214117Sjamie	if (p == NULL)
411214117Sjamie		return 0;
412214117Sjamie	cs = strrchr(p->name, '.');
413214117Sjamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
414223188Sjamie	    (TAILQ_EMPTY(&p->val) ||
415223188Sjamie	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
416223188Sjamie	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
417214117Sjamie}
418214117Sjamie
419214117Sjamie/*
420214117Sjamie * Set an integer if a parameter if it exists.
421214117Sjamie */
422214117Sjamieint
423214117Sjamieint_param(const struct cfparam *p, int *ip)
424214117Sjamie{
425223188Sjamie	if (p == NULL || TAILQ_EMPTY(&p->val))
426214117Sjamie		return 0;
427223188Sjamie	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
428214117Sjamie	return 1;
429214117Sjamie}
430214117Sjamie
431214117Sjamie/*
432214117Sjamie * Return the string value of a scalar parameter if it exists.
433214117Sjamie */
434214117Sjamieconst char *
435214117Sjamiestring_param(const struct cfparam *p)
436214117Sjamie{
437223188Sjamie	return (p && !TAILQ_EMPTY(&p->val)
438223188Sjamie	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
439214117Sjamie}
440214117Sjamie
441214117Sjamie/*
442214649Sjamie * Check syntax and values of internal parameters.  Set some internal
443214649Sjamie * parameters based on the values of others.
444214117Sjamie */
445214117Sjamieint
446214649Sjamiecheck_intparams(struct cfjail *j)
447214117Sjamie{
448214649Sjamie	struct cfparam *p;
449223327Sjamie	struct cfstring *s;
450214783Sjamie	FILE *f;
451223351Sjamie	const char *val;
452214783Sjamie	char *cs, *ep, *ln;
453223351Sjamie	size_t lnlen;
454223351Sjamie	int error;
455223351Sjamie#if defined(INET) || defined(INET6)
456223351Sjamie	struct addrinfo hints;
457223351Sjamie	struct addrinfo *ai0, *ai;
458223351Sjamie	const char *hostname;
459294196Sjamie	int gicode, defif;
460223351Sjamie#endif
461223351Sjamie#ifdef INET
462223351Sjamie	struct in_addr addr4;
463223351Sjamie	int ip4ok;
464214117Sjamie	char avalue4[INET_ADDRSTRLEN];
465223351Sjamie#endif
466214117Sjamie#ifdef INET6
467214117Sjamie	struct in6_addr addr6;
468223351Sjamie	int ip6ok;
469214117Sjamie	char avalue6[INET6_ADDRSTRLEN];
470214117Sjamie#endif
471214117Sjamie
472214117Sjamie	error = 0;
473214649Sjamie	/* Check format of boolan and integer values. */
474214649Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
475223188Sjamie		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
476223188Sjamie			val = TAILQ_LAST(&p->val, cfstrings)->s;
477214649Sjamie			if (p->flags & PF_BOOL) {
478214649Sjamie				if (strcasecmp(val, "false") &&
479214649Sjamie				    strcasecmp(val, "true") &&
480214649Sjamie				    ((void)strtol(val, &ep, 10), *ep)) {
481214649Sjamie					jail_warnx(j,
482214649Sjamie					    "%s: unknown boolean value \"%s\"",
483214649Sjamie					    p->name, val);
484214649Sjamie					error = -1;
485214649Sjamie				}
486214649Sjamie			} else {
487214649Sjamie				(void)strtol(val, &ep, 10);
488214649Sjamie				if (ep == val || *ep) {
489214649Sjamie					jail_warnx(j,
490214649Sjamie					    "%s: non-integer value \"%s\"",
491214649Sjamie					    p->name, val);
492214649Sjamie					error = -1;
493214649Sjamie				}
494214649Sjamie			}
495214649Sjamie		}
496214649Sjamie	}
497214649Sjamie
498223351Sjamie#if defined(INET) || defined(INET6)
499214117Sjamie	/*
500214117Sjamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
501214117Sjamie	 * for any IP addresses it finds.
502214117Sjamie	 */
503214649Sjamie	if (((j->flags & JF_OP_MASK) != JF_STOP ||
504214649Sjamie	    j->intparams[IP_INTERFACE] != NULL) &&
505214649Sjamie	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
506214423Sjamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
507214117Sjamie		j->intparams[IP_IP_HOSTNAME] = NULL;
508214117Sjamie		/*
509214117Sjamie		 * Silently ignore unsupported address families from
510214117Sjamie		 * DNS lookups.
511214117Sjamie		 */
512223351Sjamie#ifdef INET
513223351Sjamie		ip4ok = feature_present("inet");
514214117Sjamie#endif
515214117Sjamie#ifdef INET6
516223351Sjamie		ip6ok = feature_present("inet6");
517214117Sjamie#endif
518223351Sjamie		if (
519223351Sjamie#if defined(INET) && defined(INET6)
520223351Sjamie		    ip4ok || ip6ok
521223351Sjamie#elif defined(INET)
522223351Sjamie		    ip4ok
523223351Sjamie#elif defined(INET6)
524223351Sjamie		    ip6ok
525223351Sjamie#endif
526223351Sjamie			 ) {
527214117Sjamie			/* Look up the hostname (or get the address) */
528214117Sjamie			memset(&hints, 0, sizeof(hints));
529214117Sjamie			hints.ai_socktype = SOCK_STREAM;
530214117Sjamie			hints.ai_family =
531223351Sjamie#if defined(INET) && defined(INET6)
532223351Sjamie			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
533223351Sjamie#elif defined(INET)
534223351Sjamie			    PF_INET;
535223351Sjamie#elif defined(INET6)
536223351Sjamie			    PF_INET6;
537214117Sjamie#endif
538214649Sjamie			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
539214649Sjamie			if (gicode != 0) {
540214117Sjamie				jail_warnx(j, "host.hostname %s: %s", hostname,
541214649Sjamie				    gai_strerror(gicode));
542214117Sjamie				error = -1;
543214117Sjamie			} else {
544214117Sjamie				/*
545214117Sjamie				 * Convert the addresses to ASCII so jailparam
546214117Sjamie				 * can convert them back.  Errors are not
547214117Sjamie				 * expected here.
548214117Sjamie				 */
549214117Sjamie				for (ai = ai0; ai; ai = ai->ai_next)
550214117Sjamie					switch (ai->ai_family) {
551223351Sjamie#ifdef INET
552214117Sjamie					case AF_INET:
553214117Sjamie						memcpy(&addr4,
554214117Sjamie						    &((struct sockaddr_in *)
555214117Sjamie						    (void *)ai->ai_addr)->
556214117Sjamie						    sin_addr, sizeof(addr4));
557214117Sjamie						if (inet_ntop(AF_INET,
558214117Sjamie						    &addr4, avalue4,
559214117Sjamie						    INET_ADDRSTRLEN) == NULL)
560214117Sjamie							err(1, "inet_ntop");
561214423Sjamie						add_param(j, NULL, KP_IP4_ADDR,
562214117Sjamie						    avalue4);
563214117Sjamie						break;
564223351Sjamie#endif
565214117Sjamie#ifdef INET6
566214117Sjamie					case AF_INET6:
567214117Sjamie						memcpy(&addr6,
568214117Sjamie						    &((struct sockaddr_in6 *)
569214117Sjamie						    (void *)ai->ai_addr)->
570214117Sjamie						    sin6_addr, sizeof(addr6));
571214117Sjamie						if (inet_ntop(AF_INET6,
572214117Sjamie						    &addr6, avalue6,
573214117Sjamie						    INET6_ADDRSTRLEN) == NULL)
574214117Sjamie							err(1, "inet_ntop");
575214423Sjamie						add_param(j, NULL, KP_IP6_ADDR,
576214117Sjamie						    avalue6);
577214117Sjamie						break;
578214117Sjamie#endif
579214117Sjamie					}
580214117Sjamie				freeaddrinfo(ai0);
581214117Sjamie			}
582214117Sjamie		}
583214117Sjamie	}
584214649Sjamie
585214117Sjamie	/*
586214117Sjamie	 * IP addresses may include an interface to set that address on,
587269522Ssmh	 * a netmask/suffix for that address and options for ifconfig.
588269522Ssmh	 * These are copied to an internal command parameter and then stripped
589269522Ssmh	 * so they won't be passed on to jailparam_set.
590214117Sjamie	 */
591214117Sjamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
592223351Sjamie#ifdef INET
593223351Sjamie	if (j->intparams[KP_IP4_ADDR] != NULL) {
594223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
595214117Sjamie			cs = strchr(s->s, '|');
596214423Sjamie			if (cs || defif)
597223351Sjamie				add_param(j, NULL, IP__IP4_IFADDR, s->s);
598214423Sjamie			if (cs) {
599214423Sjamie				strcpy(s->s, cs + 1);
600214423Sjamie				s->len -= cs + 1 - s->s;
601214117Sjamie			}
602294196Sjamie			if ((cs = strchr(s->s, '/')) != NULL) {
603239621Sjamie				*cs = '\0';
604239601Sjamie				s->len = cs - s->s;
605223351Sjamie			}
606269522Ssmh			if ((cs = strchr(s->s, ' ')) != NULL) {
607269522Ssmh				*cs = '\0';
608269522Ssmh				s->len = cs - s->s;
609269522Ssmh			}
610223351Sjamie		}
611223351Sjamie	}
612214433Sjamie#endif
613214433Sjamie#ifdef INET6
614223351Sjamie	if (j->intparams[KP_IP6_ADDR] != NULL) {
615223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
616223351Sjamie			cs = strchr(s->s, '|');
617223351Sjamie			if (cs || defif)
618223351Sjamie				add_param(j, NULL, IP__IP6_IFADDR, s->s);
619223351Sjamie			if (cs) {
620223351Sjamie				strcpy(s->s, cs + 1);
621223351Sjamie				s->len -= cs + 1 - s->s;
622223351Sjamie			}
623294196Sjamie			if ((cs = strchr(s->s, '/')) != NULL) {
624239621Sjamie				*cs = '\0';
625239601Sjamie				s->len = cs - s->s;
626214117Sjamie			}
627269522Ssmh			if ((cs = strchr(s->s, ' ')) != NULL) {
628269522Ssmh				*cs = '\0';
629269522Ssmh				s->len = cs - s->s;
630269522Ssmh			}
631214117Sjamie		}
632214117Sjamie	}
633214117Sjamie#endif
634223351Sjamie#endif
635214783Sjamie
636214783Sjamie	/*
637214783Sjamie	 * Read mount.fstab file(s), and treat each line as its own mount
638214783Sjamie	 * parameter.
639214783Sjamie	 */
640214783Sjamie	if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
641223188Sjamie		TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
642214783Sjamie			if (s->len == 0)
643214783Sjamie				continue;
644214783Sjamie			f = fopen(s->s, "r");
645214783Sjamie			if (f == NULL) {
646214783Sjamie				jail_warnx(j, "mount.fstab: %s: %s",
647214783Sjamie				    s->s, strerror(errno));
648214783Sjamie				error = -1;
649214783Sjamie				continue;
650214783Sjamie			}
651214783Sjamie			while ((ln = fgetln(f, &lnlen))) {
652214783Sjamie				if ((cs = memchr(ln, '#', lnlen - 1)))
653214783Sjamie					lnlen = cs - ln + 1;
654214783Sjamie				if (ln[lnlen - 1] == '\n' ||
655214783Sjamie				    ln[lnlen - 1] == '#')
656214783Sjamie					ln[lnlen - 1] = '\0';
657214783Sjamie				else {
658214783Sjamie					cs = alloca(lnlen + 1);
659214783Sjamie					strlcpy(cs, ln, lnlen + 1);
660214783Sjamie					ln = cs;
661214783Sjamie				}
662214783Sjamie				add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
663214783Sjamie			}
664214783Sjamie			fclose(f);
665214783Sjamie		}
666214783Sjamie	}
667214783Sjamie	if (error)
668214783Sjamie		failed(j);
669214117Sjamie	return error;
670214117Sjamie}
671214117Sjamie
672214117Sjamie/*
673214117Sjamie * Import parameters into libjail's binary jailparam format.
674214117Sjamie */
675214117Sjamieint
676214117Sjamieimport_params(struct cfjail *j)
677214117Sjamie{
678214117Sjamie	struct cfparam *p;
679214117Sjamie	struct cfstring *s, *ts;
680214117Sjamie	struct jailparam *jp;
681214117Sjamie	char *value, *cs;
682214117Sjamie	size_t vallen;
683214117Sjamie	int error;
684214117Sjamie
685214117Sjamie	error = 0;
686214117Sjamie	j->njp = 0;
687214117Sjamie	TAILQ_FOREACH(p, &j->params, tq)
688214117Sjamie		if (!(p->flags & PF_INTERNAL))
689214117Sjamie			j->njp++;
690214117Sjamie	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
691214117Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
692214117Sjamie		if (p->flags & PF_INTERNAL)
693214117Sjamie			continue;
694214117Sjamie		if (jailparam_init(jp, p->name) < 0) {
695214117Sjamie			error = -1;
696214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
697241196Sjamie			jp++;
698214117Sjamie			continue;
699214117Sjamie		}
700223188Sjamie		if (TAILQ_EMPTY(&p->val))
701214117Sjamie			value = NULL;
702214117Sjamie		else if (!jp->jp_elemlen ||
703223188Sjamie			 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
704214117Sjamie			/*
705214117Sjamie			 * Scalar parameters silently discard multiple (array)
706214117Sjamie			 * values, keeping only the last value added.  This
707214117Sjamie			 * lets values added from the command line append to
708214117Sjamie			 * arrays wthout pre-checking the type.
709214117Sjamie			 */
710223188Sjamie			value = TAILQ_LAST(&p->val, cfstrings)->s;
711214117Sjamie		} else {
712214117Sjamie			/*
713214117Sjamie			 * Convert arrays into comma-separated strings, which
714214117Sjamie			 * jailparam_import will then convert back into arrays.
715214117Sjamie			 */
716214117Sjamie			vallen = 0;
717223188Sjamie			TAILQ_FOREACH(s, &p->val, tq)
718214117Sjamie				vallen += s->len + 1;
719214117Sjamie			value = alloca(vallen);
720214117Sjamie			cs = value;
721223188Sjamie			TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
722239601Sjamie				memcpy(cs, s->s, s->len);
723239621Sjamie				cs += s->len + 1;
724239621Sjamie				cs[-1] = ',';
725214117Sjamie			}
726239621Sjamie			value[vallen - 1] = '\0';
727214117Sjamie		}
728214117Sjamie		if (jailparam_import(jp, value) < 0) {
729214117Sjamie			error = -1;
730214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
731214117Sjamie		}
732214117Sjamie		jp++;
733214117Sjamie	}
734214117Sjamie	if (error) {
735214117Sjamie		jailparam_free(j->jp, j->njp);
736214117Sjamie		free(j->jp);
737214117Sjamie		j->jp = NULL;
738214117Sjamie		failed(j);
739214117Sjamie	}
740214117Sjamie	return error;
741214117Sjamie}
742214117Sjamie
743214117Sjamie/*
744214117Sjamie * Check if options are equal (with or without the "no" prefix).
745214117Sjamie */
746214117Sjamieint
747214117Sjamieequalopts(const char *opt1, const char *opt2)
748214117Sjamie{
749214117Sjamie	char *p;
750214117Sjamie
751214117Sjamie	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
752214117Sjamie	if (strcmp(opt1, opt2) == 0)
753214117Sjamie		return (1);
754214117Sjamie	/* "noopt" vs. "opt" */
755214117Sjamie	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
756214117Sjamie		return (1);
757214117Sjamie	/* "opt" vs. "noopt" */
758214117Sjamie	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
759214117Sjamie		return (1);
760214117Sjamie	while ((p = strchr(opt1, '.')) != NULL &&
761214117Sjamie	    !strncmp(opt1, opt2, ++p - opt1)) {
762214117Sjamie		opt2 += p - opt1;
763214117Sjamie		opt1 = p;
764214117Sjamie		/* "foo.noopt" vs. "foo.opt" */
765214117Sjamie		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
766214117Sjamie			return (1);
767214117Sjamie		/* "foo.opt" vs. "foo.noopt" */
768214117Sjamie		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
769214117Sjamie			return (1);
770214117Sjamie	}
771214117Sjamie	return (0);
772214117Sjamie}
773214117Sjamie
774214117Sjamie/*
775214117Sjamie * See if a jail name matches a wildcard.
776214117Sjamie */
777214117Sjamieint
778214117Sjamiewild_jail_match(const char *jname, const char *wname)
779214117Sjamie{
780214117Sjamie	const char *jc, *jd, *wc, *wd;
781214117Sjamie
782214117Sjamie	/*
783214117Sjamie	 * A non-final "*" component in the wild name matches a single jail
784214117Sjamie	 * component, and a final "*" matches one or more jail components.
785214117Sjamie	 */
786214117Sjamie	for (jc = jname, wc = wname;
787214117Sjamie	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
788214117Sjamie	     jc = jd + 1, wc = wd + 1)
789214117Sjamie		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
790214117Sjamie			return 0;
791214117Sjamie	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
792214117Sjamie}
793214117Sjamie
794214117Sjamie/*
795214117Sjamie * Return if a jail name is a wildcard.
796214117Sjamie */
797214117Sjamieint
798214117Sjamiewild_jail_name(const char *wname)
799214117Sjamie{
800214117Sjamie	const char *wc;
801214117Sjamie
802214117Sjamie	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
803214117Sjamie		if ((wc == wname || wc[-1] == '.') &&
804214117Sjamie		    (wc[1] == '\0' || wc[1] == '.'))
805214117Sjamie			return 1;
806214117Sjamie	return 0;
807214117Sjamie}
808214117Sjamie
809214117Sjamie/*
810214117Sjamie * Free a parameter record and all its strings and variables.
811214117Sjamie */
812214117Sjamiestatic void
813214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p)
814214117Sjamie{
815214117Sjamie	free(p->name);
816214117Sjamie	free_param_strings(p);
817214117Sjamie	TAILQ_REMOVE(pp, p, tq);
818214117Sjamie	free(p);
819214117Sjamie}
820214117Sjamie
821214117Sjamiestatic void
822214117Sjamiefree_param_strings(struct cfparam *p)
823214117Sjamie{
824214117Sjamie	struct cfstring *s;
825214117Sjamie	struct cfvar *v;
826214117Sjamie
827223188Sjamie	while ((s = TAILQ_FIRST(&p->val))) {
828214117Sjamie		free(s->s);
829214117Sjamie		while ((v = STAILQ_FIRST(&s->vars))) {
830214117Sjamie			free(v->name);
831214117Sjamie			STAILQ_REMOVE_HEAD(&s->vars, tq);
832214117Sjamie			free(v);
833214117Sjamie		}
834223188Sjamie		TAILQ_REMOVE(&p->val, s, tq);
835214117Sjamie		free(s);
836214117Sjamie	}
837214117Sjamie}
838