1214117Sjamie/*-
2223190Sjamie * Copyright (c) 2011 James Gritton
3214117Sjamie * All rights reserved.
4214117Sjamie *
5214117Sjamie * Redistribution and use in source and binary forms, with or without
6214117Sjamie * modification, are permitted provided that the following conditions
7214117Sjamie * are met:
8214117Sjamie * 1. Redistributions of source code must retain the above copyright
9214117Sjamie *    notice, this list of conditions and the following disclaimer.
10214117Sjamie * 2. Redistributions in binary form must reproduce the above copyright
11214117Sjamie *    notice, this list of conditions and the following disclaimer in the
12214117Sjamie *    documentation and/or other materials provided with the distribution.
13214117Sjamie *
14214117Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15214117Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16214117Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17214117Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18214117Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19214117Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20214117Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21214117Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22214117Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23214117Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24214117Sjamie * SUCH DAMAGE.
25214117Sjamie */
26214117Sjamie
27214117Sjamie#include <sys/cdefs.h>
28214117Sjamie__FBSDID("$FreeBSD$");
29214117Sjamie
30214117Sjamie#include <sys/types.h>
31214783Sjamie#include <sys/errno.h>
32214117Sjamie#include <sys/socket.h>
33214117Sjamie#include <sys/sysctl.h>
34214117Sjamie
35214117Sjamie#include <arpa/inet.h>
36214117Sjamie#include <netinet/in.h>
37214117Sjamie
38214117Sjamie#include <err.h>
39214117Sjamie#include <netdb.h>
40214117Sjamie#include <stdio.h>
41214117Sjamie#include <stdlib.h>
42214117Sjamie#include <string.h>
43223351Sjamie#include <unistd.h>
44214117Sjamie
45214117Sjamie#include "jailp.h"
46214117Sjamie
47214117Sjamiestruct ipspec {
48214117Sjamie	const char	*name;
49214117Sjamie	unsigned	flags;
50214117Sjamie};
51214117Sjamie
52214117Sjamieextern FILE *yyin;
53214117Sjamieextern int yynerrs;
54214117Sjamie
55235789Sbaptextern int yyparse(void);
56235789Sbapt
57214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
58214117Sjamie
59214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p);
60214117Sjamiestatic void free_param_strings(struct cfparam *p);
61214117Sjamie
62214117Sjamiestatic const struct ipspec intparams[] = {
63214423Sjamie    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
64214423Sjamie    [IP_COMMAND] =		{"command",		PF_INTERNAL},
65214423Sjamie    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
66214423Sjamie    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
67214423Sjamie    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
68214423Sjamie    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
69214423Sjamie    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
70214423Sjamie    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
71214423Sjamie    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
72214423Sjamie    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
73214423Sjamie    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
74214423Sjamie    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
75214423Sjamie    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
76214423Sjamie    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
77214423Sjamie							PF_INTERNAL | PF_BOOL},
78214423Sjamie    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
79214423Sjamie    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
80223351Sjamie#if defined(INET) || defined(INET6)
81214423Sjamie    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
82214423Sjamie    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
83223351Sjamie#endif
84248854Sjamie    [IP_MOUNT] =		{"mount",		PF_INTERNAL | PF_REV},
85214423Sjamie    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
86256387Shrs    [IP_MOUNT_FDESCFS] =	{"mount.fdescfs",	PF_INTERNAL | PF_BOOL},
87278484Sjamie    [IP_MOUNT_PROCFS] =		{"mount.procfs",	PF_INTERNAL | PF_BOOL},
88214423Sjamie    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
89214423Sjamie    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
90214423Sjamie    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
91223351Sjamie#ifdef INET
92248854Sjamie    [IP__IP4_IFADDR] =		{"ip4.addr",	PF_INTERNAL | PF_CONV | PF_REV},
93223351Sjamie#endif
94214117Sjamie#ifdef INET6
95248854Sjamie    [IP__IP6_IFADDR] =		{"ip6.addr",	PF_INTERNAL | PF_CONV | PF_REV},
96214117Sjamie#endif
97248854Sjamie    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",	PF_INTERNAL | PF_CONV | PF_REV},
98223189Sjamie    [IP__OP] =			{NULL,			PF_CONV},
99214423Sjamie    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
100214423Sjamie    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
101214423Sjamie    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
102214423Sjamie    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
103214423Sjamie    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
104214423Sjamie    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
105232242Sjamie    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
106214423Sjamie    [KP_ENFORCE_STATFS] =	{"enforce_statfs",	0},
107214423Sjamie    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
108223351Sjamie#ifdef INET
109214423Sjamie    [KP_IP4_ADDR] =		{"ip4.addr",		0},
110223351Sjamie#endif
111214423Sjamie#ifdef INET6
112214423Sjamie    [KP_IP6_ADDR] =		{"ip6.addr",		0},
113214423Sjamie#endif
114285827Shrs    [KP_JID] =			{"jid",			PF_IMMUTABLE},
115285827Shrs    [KP_NAME] =			{"name",		PF_IMMUTABLE},
116214423Sjamie    [KP_PATH] =			{"path",		0},
117214423Sjamie    [KP_PERSIST] =		{"persist",		0},
118214423Sjamie    [KP_SECURELEVEL] =		{"securelevel",		0},
119214423Sjamie    [KP_VNET] =			{"vnet",		0},
120214117Sjamie};
121214117Sjamie
122214117Sjamie/*
123214117Sjamie * Parse the jail configuration file.
124214117Sjamie */
125214117Sjamievoid
126214117Sjamieload_config(void)
127214117Sjamie{
128214117Sjamie	struct cfjails wild;
129214117Sjamie	struct cfparams opp;
130214117Sjamie	struct cfjail *j, *tj, *wj;
131214117Sjamie	struct cfparam *p, *vp, *tp;
132214117Sjamie	struct cfstring *s, *vs, *ns;
133285827Shrs	struct cfvar *v, *vv;
134214117Sjamie	char *ep;
135214117Sjamie	int did_self, jseq, pgen;
136214117Sjamie
137214117Sjamie	if (!strcmp(cfname, "-")) {
138214117Sjamie		cfname = "STDIN";
139214117Sjamie		yyin = stdin;
140214117Sjamie	} else {
141214117Sjamie		yyin = fopen(cfname, "r");
142214117Sjamie		if (!yyin)
143214117Sjamie			err(1, "%s", cfname);
144214117Sjamie	}
145214117Sjamie	if (yyparse() || yynerrs)
146214117Sjamie		exit(1);
147214117Sjamie
148214117Sjamie	/* Separate the wildcard jails out from the actual jails. */
149214117Sjamie	jseq = 0;
150214117Sjamie	TAILQ_INIT(&wild);
151214117Sjamie	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
152214117Sjamie		j->seq = ++jseq;
153214117Sjamie		if (wild_jail_name(j->name))
154214117Sjamie			requeue(j, &wild);
155214117Sjamie	}
156214117Sjamie
157214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq) {
158214117Sjamie		/* Set aside the jail's parameters. */
159214117Sjamie		TAILQ_INIT(&opp);
160214117Sjamie		TAILQ_CONCAT(&opp, &j->params, tq);
161214117Sjamie		/*
162214117Sjamie		 * The jail name implies its "name" or "jid" parameter,
163214117Sjamie		 * though they may also be explicitly set later on.
164214117Sjamie		 */
165214117Sjamie		add_param(j, NULL,
166214423Sjamie		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
167214117Sjamie		    j->name);
168214117Sjamie		/*
169214117Sjamie		 * Collect parameters for the jail, global parameters/variables,
170214117Sjamie		 * and any matching wildcard jails.
171214117Sjamie		 */
172214117Sjamie		did_self = 0;
173214117Sjamie		TAILQ_FOREACH(wj, &wild, tq) {
174214117Sjamie			if (j->seq < wj->seq && !did_self) {
175214117Sjamie				TAILQ_FOREACH(p, &opp, tq)
176214423Sjamie					add_param(j, p, 0, NULL);
177214117Sjamie				did_self = 1;
178214117Sjamie			}
179214117Sjamie			if (wild_jail_match(j->name, wj->name))
180214117Sjamie				TAILQ_FOREACH(p, &wj->params, tq)
181214423Sjamie					add_param(j, p, 0, NULL);
182214117Sjamie		}
183214117Sjamie		if (!did_self)
184214117Sjamie			TAILQ_FOREACH(p, &opp, tq)
185214423Sjamie				add_param(j, p, 0, NULL);
186214117Sjamie
187214117Sjamie		/* Resolve any variable substitutions. */
188214117Sjamie		pgen = 0;
189214117Sjamie		TAILQ_FOREACH(p, &j->params, tq) {
190214117Sjamie		    p->gen = ++pgen;
191214117Sjamie		find_vars:
192223188Sjamie		    TAILQ_FOREACH(s, &p->val, tq) {
193214117Sjamie			while ((v = STAILQ_FIRST(&s->vars))) {
194214117Sjamie				TAILQ_FOREACH(vp, &j->params, tq)
195214117Sjamie					if (!strcmp(vp->name, v->name))
196214117Sjamie						break;
197214117Sjamie				if (!vp) {
198214117Sjamie					jail_warnx(j,
199214117Sjamie					    "%s: variable \"%s\" not found",
200214117Sjamie					    p->name, v->name);
201214117Sjamie				bad_var:
202214117Sjamie					j->flags |= JF_FAILED;
203214117Sjamie					TAILQ_FOREACH(vp, &j->params, tq)
204214117Sjamie						if (vp->gen == pgen)
205214117Sjamie							vp->flags |= PF_BAD;
206214117Sjamie					goto free_var;
207214117Sjamie				}
208214117Sjamie				if (vp->flags & PF_BAD)
209214117Sjamie					goto bad_var;
210214117Sjamie				if (vp->gen == pgen) {
211214117Sjamie					jail_warnx(j, "%s: variable loop",
212214117Sjamie					    v->name);
213214117Sjamie					goto bad_var;
214214117Sjamie				}
215223188Sjamie				TAILQ_FOREACH(vs, &vp->val, tq)
216214117Sjamie					if (!STAILQ_EMPTY(&vs->vars)) {
217214117Sjamie						vp->gen = pgen;
218214117Sjamie						TAILQ_REMOVE(&j->params, vp,
219214117Sjamie						    tq);
220214117Sjamie						TAILQ_INSERT_BEFORE(p, vp, tq);
221214117Sjamie						p = vp;
222214117Sjamie						goto find_vars;
223214117Sjamie					}
224223188Sjamie				vs = TAILQ_FIRST(&vp->val);
225223188Sjamie				if (TAILQ_NEXT(vs, tq) != NULL &&
226214117Sjamie				    (s->s[0] != '\0' ||
227214117Sjamie				     STAILQ_NEXT(v, tq))) {
228214117Sjamie					jail_warnx(j, "%s: array cannot be "
229214117Sjamie					    "substituted inline",
230214117Sjamie					    p->name);
231214117Sjamie					goto bad_var;
232214117Sjamie				}
233214117Sjamie				s->s = erealloc(s->s, s->len + vs->len + 1);
234285827Shrs				memmove(s->s + v->pos + vs->len,
235285827Shrs				    s->s + v->pos,
236285827Shrs				    s->len - v->pos + 1);
237285827Shrs				memcpy(s->s + v->pos, vs->s, vs->len);
238285827Shrs				vv = v;
239285827Shrs				while ((vv = STAILQ_NEXT(vv, tq)))
240285827Shrs					vv->pos += vs->len;
241214117Sjamie				s->len += vs->len;
242223188Sjamie				while ((vs = TAILQ_NEXT(vs, tq))) {
243214117Sjamie					ns = emalloc(sizeof(struct cfstring));
244214117Sjamie					ns->s = estrdup(vs->s);
245214117Sjamie					ns->len = vs->len;
246214117Sjamie					STAILQ_INIT(&ns->vars);
247223188Sjamie					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
248214117Sjamie					s = ns;
249214117Sjamie				}
250214117Sjamie			free_var:
251214117Sjamie				free(v->name);
252214117Sjamie				STAILQ_REMOVE_HEAD(&s->vars, tq);
253214117Sjamie				free(v);
254214117Sjamie			}
255214117Sjamie		    }
256214117Sjamie		}
257214117Sjamie
258214117Sjamie		/* Free the jail's original parameter list and any variables. */
259214117Sjamie		while ((p = TAILQ_FIRST(&opp)))
260214117Sjamie			free_param(&opp, p);
261214117Sjamie		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
262214117Sjamie			if (p->flags & PF_VAR)
263214117Sjamie				free_param(&j->params, p);
264214117Sjamie	}
265214117Sjamie	while ((wj = TAILQ_FIRST(&wild))) {
266214117Sjamie		free(wj->name);
267214117Sjamie		while ((p = TAILQ_FIRST(&wj->params)))
268214117Sjamie			free_param(&wj->params, p);
269214117Sjamie		TAILQ_REMOVE(&wild, wj, tq);
270214117Sjamie	}
271214117Sjamie}
272214117Sjamie
273214117Sjamie/*
274214117Sjamie * Create a new jail record.
275214117Sjamie */
276214117Sjamiestruct cfjail *
277214117Sjamieadd_jail(void)
278214117Sjamie{
279214117Sjamie	struct cfjail *j;
280214117Sjamie
281214117Sjamie	j = emalloc(sizeof(struct cfjail));
282214117Sjamie	memset(j, 0, sizeof(struct cfjail));
283214117Sjamie	TAILQ_INIT(&j->params);
284214117Sjamie	STAILQ_INIT(&j->dep[DEP_FROM]);
285214117Sjamie	STAILQ_INIT(&j->dep[DEP_TO]);
286214117Sjamie	j->queue = &cfjails;
287214117Sjamie	TAILQ_INSERT_TAIL(&cfjails, j, tq);
288214117Sjamie	return j;
289214117Sjamie}
290214117Sjamie
291214117Sjamie/*
292214117Sjamie * Add a parameter to a jail.
293214117Sjamie */
294214117Sjamievoid
295214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
296214117Sjamie    const char *value)
297214117Sjamie{
298214117Sjamie	struct cfstrings nss;
299214117Sjamie	struct cfparam *dp, *np;
300214117Sjamie	struct cfstring *s, *ns;
301214117Sjamie	struct cfvar *v, *nv;
302214423Sjamie	const char *name;
303214423Sjamie	char *cs, *tname;
304214117Sjamie	unsigned flags;
305214117Sjamie
306214117Sjamie	if (j == NULL) {
307214117Sjamie		/* Create a single anonymous jail if one doesn't yet exist. */
308214117Sjamie		j = TAILQ_LAST(&cfjails, cfjails);
309214117Sjamie		if (j == NULL)
310214117Sjamie			j = add_jail();
311214117Sjamie	}
312223188Sjamie	TAILQ_INIT(&nss);
313214117Sjamie	if (p != NULL) {
314214117Sjamie		name = p->name;
315214117Sjamie		flags = p->flags;
316214117Sjamie		/*
317214117Sjamie		 * Make a copy of the parameter's string list,
318214117Sjamie		 * which may be freed if it's overridden later.
319214117Sjamie		 */
320223188Sjamie		TAILQ_FOREACH(s, &p->val, tq) {
321214117Sjamie			ns = emalloc(sizeof(struct cfstring));
322214117Sjamie			ns->s = estrdup(s->s);
323214117Sjamie			ns->len = s->len;
324214117Sjamie			STAILQ_INIT(&ns->vars);
325214117Sjamie			STAILQ_FOREACH(v, &s->vars, tq) {
326214117Sjamie				nv = emalloc(sizeof(struct cfvar));
327214117Sjamie				nv->name = strdup(v->name);
328214117Sjamie				nv->pos = v->pos;
329214117Sjamie				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
330214117Sjamie			}
331223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
332214117Sjamie		}
333214117Sjamie	} else {
334214117Sjamie		flags = PF_APPEND;
335234988Sjamie		if (ipnum != IP__NULL) {
336214423Sjamie			name = intparams[ipnum].name;
337214423Sjamie			flags |= intparams[ipnum].flags;
338214423Sjamie		} else if ((cs = strchr(value, '='))) {
339214423Sjamie			tname = alloca(cs - value + 1);
340214423Sjamie			strlcpy(tname, value, cs - value + 1);
341214423Sjamie			name = tname;
342214423Sjamie			value = cs + 1;
343214423Sjamie		} else {
344214423Sjamie			name = value;
345214423Sjamie			value = NULL;
346214423Sjamie		}
347214117Sjamie		if (value != NULL) {
348214117Sjamie			ns = emalloc(sizeof(struct cfstring));
349214117Sjamie			ns->s = estrdup(value);
350214117Sjamie			ns->len = strlen(value);
351214117Sjamie			STAILQ_INIT(&ns->vars);
352223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
353214117Sjamie		}
354214117Sjamie	}
355214117Sjamie
356214117Sjamie	/* See if this parameter has already been added. */
357234988Sjamie	if (ipnum != IP__NULL)
358214423Sjamie		dp = j->intparams[ipnum];
359214423Sjamie	else
360214423Sjamie		TAILQ_FOREACH(dp, &j->params, tq)
361214423Sjamie			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
362214423Sjamie				break;
363214423Sjamie	if (dp != NULL) {
364214423Sjamie		/* Found it - append or replace. */
365285827Shrs		if (dp->flags & PF_IMMUTABLE) {
366285827Shrs			jail_warnx(j, "cannot redefine variable \"%s\".",
367285827Shrs			    dp->name);
368285827Shrs			return;
369285827Shrs		}
370214423Sjamie		if (strcmp(dp->name, name)) {
371214423Sjamie			free(dp->name);
372214423Sjamie			dp->name = estrdup(name);
373214117Sjamie		}
374223188Sjamie		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
375214423Sjamie			free_param_strings(dp);
376223188Sjamie		TAILQ_CONCAT(&dp->val, &nss, tq);
377214423Sjamie		dp->flags |= flags;
378214423Sjamie	} else {
379214117Sjamie		/* Not found - add it. */
380214117Sjamie		np = emalloc(sizeof(struct cfparam));
381214117Sjamie		np->name = estrdup(name);
382223188Sjamie		TAILQ_INIT(&np->val);
383223188Sjamie		TAILQ_CONCAT(&np->val, &nss, tq);
384214117Sjamie		np->flags = flags;
385214117Sjamie		np->gen = 0;
386214117Sjamie		TAILQ_INSERT_TAIL(&j->params, np, tq);
387234988Sjamie		if (ipnum != IP__NULL)
388214423Sjamie			j->intparams[ipnum] = np;
389214423Sjamie		else
390234988Sjamie			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
391214423Sjamie				if (!(intparams[ipnum].flags & PF_CONV) &&
392214423Sjamie				    equalopts(name, intparams[ipnum].name)) {
393214423Sjamie					j->intparams[ipnum] = np;
394214423Sjamie					np->flags |= intparams[ipnum].flags;
395214423Sjamie					break;
396214423Sjamie				}
397214117Sjamie	}
398214117Sjamie}
399214117Sjamie
400214117Sjamie/*
401214117Sjamie * Return if a boolean parameter exists and is true.
402214117Sjamie */
403214117Sjamieint
404214117Sjamiebool_param(const struct cfparam *p)
405214117Sjamie{
406214117Sjamie	const char *cs;
407214117Sjamie
408214117Sjamie	if (p == NULL)
409214117Sjamie		return 0;
410214117Sjamie	cs = strrchr(p->name, '.');
411214117Sjamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
412223188Sjamie	    (TAILQ_EMPTY(&p->val) ||
413223188Sjamie	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
414223188Sjamie	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
415214117Sjamie}
416214117Sjamie
417214117Sjamie/*
418214117Sjamie * Set an integer if a parameter if it exists.
419214117Sjamie */
420214117Sjamieint
421214117Sjamieint_param(const struct cfparam *p, int *ip)
422214117Sjamie{
423223188Sjamie	if (p == NULL || TAILQ_EMPTY(&p->val))
424214117Sjamie		return 0;
425223188Sjamie	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
426214117Sjamie	return 1;
427214117Sjamie}
428214117Sjamie
429214117Sjamie/*
430214117Sjamie * Return the string value of a scalar parameter if it exists.
431214117Sjamie */
432214117Sjamieconst char *
433214117Sjamiestring_param(const struct cfparam *p)
434214117Sjamie{
435223188Sjamie	return (p && !TAILQ_EMPTY(&p->val)
436223188Sjamie	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
437214117Sjamie}
438214117Sjamie
439214117Sjamie/*
440214649Sjamie * Check syntax and values of internal parameters.  Set some internal
441214649Sjamie * parameters based on the values of others.
442214117Sjamie */
443214117Sjamieint
444214649Sjamiecheck_intparams(struct cfjail *j)
445214117Sjamie{
446214649Sjamie	struct cfparam *p;
447223327Sjamie	struct cfstring *s;
448214783Sjamie	FILE *f;
449223351Sjamie	const char *val;
450214783Sjamie	char *cs, *ep, *ln;
451223351Sjamie	size_t lnlen;
452223351Sjamie	int error;
453223351Sjamie#if defined(INET) || defined(INET6)
454223351Sjamie	struct addrinfo hints;
455223351Sjamie	struct addrinfo *ai0, *ai;
456223351Sjamie	const char *hostname;
457294484Sjamie	int gicode, defif;
458223351Sjamie#endif
459223351Sjamie#ifdef INET
460223351Sjamie	struct in_addr addr4;
461223351Sjamie	int ip4ok;
462214117Sjamie	char avalue4[INET_ADDRSTRLEN];
463223351Sjamie#endif
464214117Sjamie#ifdef INET6
465214117Sjamie	struct in6_addr addr6;
466223351Sjamie	int ip6ok;
467214117Sjamie	char avalue6[INET6_ADDRSTRLEN];
468214117Sjamie#endif
469214117Sjamie
470214117Sjamie	error = 0;
471214649Sjamie	/* Check format of boolan and integer values. */
472214649Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
473223188Sjamie		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
474223188Sjamie			val = TAILQ_LAST(&p->val, cfstrings)->s;
475214649Sjamie			if (p->flags & PF_BOOL) {
476214649Sjamie				if (strcasecmp(val, "false") &&
477214649Sjamie				    strcasecmp(val, "true") &&
478214649Sjamie				    ((void)strtol(val, &ep, 10), *ep)) {
479214649Sjamie					jail_warnx(j,
480214649Sjamie					    "%s: unknown boolean value \"%s\"",
481214649Sjamie					    p->name, val);
482214649Sjamie					error = -1;
483214649Sjamie				}
484214649Sjamie			} else {
485214649Sjamie				(void)strtol(val, &ep, 10);
486214649Sjamie				if (ep == val || *ep) {
487214649Sjamie					jail_warnx(j,
488214649Sjamie					    "%s: non-integer value \"%s\"",
489214649Sjamie					    p->name, val);
490214649Sjamie					error = -1;
491214649Sjamie				}
492214649Sjamie			}
493214649Sjamie		}
494214649Sjamie	}
495214649Sjamie
496223351Sjamie#if defined(INET) || defined(INET6)
497214117Sjamie	/*
498214117Sjamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
499214117Sjamie	 * for any IP addresses it finds.
500214117Sjamie	 */
501214649Sjamie	if (((j->flags & JF_OP_MASK) != JF_STOP ||
502214649Sjamie	    j->intparams[IP_INTERFACE] != NULL) &&
503214649Sjamie	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
504214423Sjamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
505214117Sjamie		j->intparams[IP_IP_HOSTNAME] = NULL;
506214117Sjamie		/*
507214117Sjamie		 * Silently ignore unsupported address families from
508214117Sjamie		 * DNS lookups.
509214117Sjamie		 */
510223351Sjamie#ifdef INET
511223351Sjamie		ip4ok = feature_present("inet");
512214117Sjamie#endif
513214117Sjamie#ifdef INET6
514223351Sjamie		ip6ok = feature_present("inet6");
515214117Sjamie#endif
516223351Sjamie		if (
517223351Sjamie#if defined(INET) && defined(INET6)
518223351Sjamie		    ip4ok || ip6ok
519223351Sjamie#elif defined(INET)
520223351Sjamie		    ip4ok
521223351Sjamie#elif defined(INET6)
522223351Sjamie		    ip6ok
523223351Sjamie#endif
524223351Sjamie			 ) {
525214117Sjamie			/* Look up the hostname (or get the address) */
526214117Sjamie			memset(&hints, 0, sizeof(hints));
527214117Sjamie			hints.ai_socktype = SOCK_STREAM;
528214117Sjamie			hints.ai_family =
529223351Sjamie#if defined(INET) && defined(INET6)
530223351Sjamie			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
531223351Sjamie#elif defined(INET)
532223351Sjamie			    PF_INET;
533223351Sjamie#elif defined(INET6)
534223351Sjamie			    PF_INET6;
535214117Sjamie#endif
536214649Sjamie			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
537214649Sjamie			if (gicode != 0) {
538214117Sjamie				jail_warnx(j, "host.hostname %s: %s", hostname,
539214649Sjamie				    gai_strerror(gicode));
540214117Sjamie				error = -1;
541214117Sjamie			} else {
542214117Sjamie				/*
543214117Sjamie				 * Convert the addresses to ASCII so jailparam
544214117Sjamie				 * can convert them back.  Errors are not
545214117Sjamie				 * expected here.
546214117Sjamie				 */
547214117Sjamie				for (ai = ai0; ai; ai = ai->ai_next)
548214117Sjamie					switch (ai->ai_family) {
549223351Sjamie#ifdef INET
550214117Sjamie					case AF_INET:
551214117Sjamie						memcpy(&addr4,
552214117Sjamie						    &((struct sockaddr_in *)
553214117Sjamie						    (void *)ai->ai_addr)->
554214117Sjamie						    sin_addr, sizeof(addr4));
555214117Sjamie						if (inet_ntop(AF_INET,
556214117Sjamie						    &addr4, avalue4,
557214117Sjamie						    INET_ADDRSTRLEN) == NULL)
558214117Sjamie							err(1, "inet_ntop");
559214423Sjamie						add_param(j, NULL, KP_IP4_ADDR,
560214117Sjamie						    avalue4);
561214117Sjamie						break;
562223351Sjamie#endif
563214117Sjamie#ifdef INET6
564214117Sjamie					case AF_INET6:
565214117Sjamie						memcpy(&addr6,
566214117Sjamie						    &((struct sockaddr_in6 *)
567214117Sjamie						    (void *)ai->ai_addr)->
568214117Sjamie						    sin6_addr, sizeof(addr6));
569214117Sjamie						if (inet_ntop(AF_INET6,
570214117Sjamie						    &addr6, avalue6,
571214117Sjamie						    INET6_ADDRSTRLEN) == NULL)
572214117Sjamie							err(1, "inet_ntop");
573214423Sjamie						add_param(j, NULL, KP_IP6_ADDR,
574214117Sjamie						    avalue6);
575214117Sjamie						break;
576214117Sjamie#endif
577214117Sjamie					}
578214117Sjamie				freeaddrinfo(ai0);
579214117Sjamie			}
580214117Sjamie		}
581214117Sjamie	}
582214649Sjamie
583214117Sjamie	/*
584214117Sjamie	 * IP addresses may include an interface to set that address on,
585269805Ssmh	 * a netmask/suffix for that address and options for ifconfig.
586269805Ssmh	 * These are copied to an internal command parameter and then stripped
587269805Ssmh	 * so they won't be passed on to jailparam_set.
588214117Sjamie	 */
589214117Sjamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
590223351Sjamie#ifdef INET
591223351Sjamie	if (j->intparams[KP_IP4_ADDR] != NULL) {
592223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
593214117Sjamie			cs = strchr(s->s, '|');
594214423Sjamie			if (cs || defif)
595223351Sjamie				add_param(j, NULL, IP__IP4_IFADDR, s->s);
596214423Sjamie			if (cs) {
597214423Sjamie				strcpy(s->s, cs + 1);
598214423Sjamie				s->len -= cs + 1 - s->s;
599214117Sjamie			}
600294484Sjamie			if ((cs = strchr(s->s, '/')) != NULL) {
601239621Sjamie				*cs = '\0';
602239601Sjamie				s->len = cs - s->s;
603223351Sjamie			}
604269805Ssmh			if ((cs = strchr(s->s, ' ')) != NULL) {
605269805Ssmh				*cs = '\0';
606269805Ssmh				s->len = cs - s->s;
607269805Ssmh			}
608223351Sjamie		}
609223351Sjamie	}
610214433Sjamie#endif
611214433Sjamie#ifdef INET6
612223351Sjamie	if (j->intparams[KP_IP6_ADDR] != NULL) {
613223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
614223351Sjamie			cs = strchr(s->s, '|');
615223351Sjamie			if (cs || defif)
616223351Sjamie				add_param(j, NULL, IP__IP6_IFADDR, s->s);
617223351Sjamie			if (cs) {
618223351Sjamie				strcpy(s->s, cs + 1);
619223351Sjamie				s->len -= cs + 1 - s->s;
620223351Sjamie			}
621294484Sjamie			if ((cs = strchr(s->s, '/')) != NULL) {
622239621Sjamie				*cs = '\0';
623239601Sjamie				s->len = cs - s->s;
624214117Sjamie			}
625269805Ssmh			if ((cs = strchr(s->s, ' ')) != NULL) {
626269805Ssmh				*cs = '\0';
627269805Ssmh				s->len = cs - s->s;
628269805Ssmh			}
629214117Sjamie		}
630214117Sjamie	}
631214117Sjamie#endif
632223351Sjamie#endif
633214783Sjamie
634214783Sjamie	/*
635214783Sjamie	 * Read mount.fstab file(s), and treat each line as its own mount
636214783Sjamie	 * parameter.
637214783Sjamie	 */
638214783Sjamie	if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
639223188Sjamie		TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
640214783Sjamie			if (s->len == 0)
641214783Sjamie				continue;
642214783Sjamie			f = fopen(s->s, "r");
643214783Sjamie			if (f == NULL) {
644214783Sjamie				jail_warnx(j, "mount.fstab: %s: %s",
645214783Sjamie				    s->s, strerror(errno));
646214783Sjamie				error = -1;
647214783Sjamie				continue;
648214783Sjamie			}
649214783Sjamie			while ((ln = fgetln(f, &lnlen))) {
650214783Sjamie				if ((cs = memchr(ln, '#', lnlen - 1)))
651214783Sjamie					lnlen = cs - ln + 1;
652214783Sjamie				if (ln[lnlen - 1] == '\n' ||
653214783Sjamie				    ln[lnlen - 1] == '#')
654214783Sjamie					ln[lnlen - 1] = '\0';
655214783Sjamie				else {
656214783Sjamie					cs = alloca(lnlen + 1);
657214783Sjamie					strlcpy(cs, ln, lnlen + 1);
658214783Sjamie					ln = cs;
659214783Sjamie				}
660214783Sjamie				add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
661214783Sjamie			}
662214783Sjamie			fclose(f);
663214783Sjamie		}
664214783Sjamie	}
665214783Sjamie	if (error)
666214783Sjamie		failed(j);
667214117Sjamie	return error;
668214117Sjamie}
669214117Sjamie
670214117Sjamie/*
671214117Sjamie * Import parameters into libjail's binary jailparam format.
672214117Sjamie */
673214117Sjamieint
674214117Sjamieimport_params(struct cfjail *j)
675214117Sjamie{
676214117Sjamie	struct cfparam *p;
677214117Sjamie	struct cfstring *s, *ts;
678214117Sjamie	struct jailparam *jp;
679214117Sjamie	char *value, *cs;
680214117Sjamie	size_t vallen;
681214117Sjamie	int error;
682214117Sjamie
683214117Sjamie	error = 0;
684214117Sjamie	j->njp = 0;
685214117Sjamie	TAILQ_FOREACH(p, &j->params, tq)
686214117Sjamie		if (!(p->flags & PF_INTERNAL))
687214117Sjamie			j->njp++;
688214117Sjamie	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
689214117Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
690214117Sjamie		if (p->flags & PF_INTERNAL)
691214117Sjamie			continue;
692214117Sjamie		if (jailparam_init(jp, p->name) < 0) {
693214117Sjamie			error = -1;
694214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
695241196Sjamie			jp++;
696214117Sjamie			continue;
697214117Sjamie		}
698223188Sjamie		if (TAILQ_EMPTY(&p->val))
699214117Sjamie			value = NULL;
700214117Sjamie		else if (!jp->jp_elemlen ||
701223188Sjamie			 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
702214117Sjamie			/*
703214117Sjamie			 * Scalar parameters silently discard multiple (array)
704214117Sjamie			 * values, keeping only the last value added.  This
705214117Sjamie			 * lets values added from the command line append to
706214117Sjamie			 * arrays wthout pre-checking the type.
707214117Sjamie			 */
708223188Sjamie			value = TAILQ_LAST(&p->val, cfstrings)->s;
709214117Sjamie		} else {
710214117Sjamie			/*
711214117Sjamie			 * Convert arrays into comma-separated strings, which
712214117Sjamie			 * jailparam_import will then convert back into arrays.
713214117Sjamie			 */
714214117Sjamie			vallen = 0;
715223188Sjamie			TAILQ_FOREACH(s, &p->val, tq)
716214117Sjamie				vallen += s->len + 1;
717214117Sjamie			value = alloca(vallen);
718214117Sjamie			cs = value;
719223188Sjamie			TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
720239601Sjamie				memcpy(cs, s->s, s->len);
721239621Sjamie				cs += s->len + 1;
722239621Sjamie				cs[-1] = ',';
723214117Sjamie			}
724239621Sjamie			value[vallen - 1] = '\0';
725214117Sjamie		}
726214117Sjamie		if (jailparam_import(jp, value) < 0) {
727214117Sjamie			error = -1;
728214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
729214117Sjamie		}
730214117Sjamie		jp++;
731214117Sjamie	}
732214117Sjamie	if (error) {
733214117Sjamie		jailparam_free(j->jp, j->njp);
734214117Sjamie		free(j->jp);
735214117Sjamie		j->jp = NULL;
736214117Sjamie		failed(j);
737214117Sjamie	}
738214117Sjamie	return error;
739214117Sjamie}
740214117Sjamie
741214117Sjamie/*
742214117Sjamie * Check if options are equal (with or without the "no" prefix).
743214117Sjamie */
744214117Sjamieint
745214117Sjamieequalopts(const char *opt1, const char *opt2)
746214117Sjamie{
747214117Sjamie	char *p;
748214117Sjamie
749214117Sjamie	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
750214117Sjamie	if (strcmp(opt1, opt2) == 0)
751214117Sjamie		return (1);
752214117Sjamie	/* "noopt" vs. "opt" */
753214117Sjamie	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
754214117Sjamie		return (1);
755214117Sjamie	/* "opt" vs. "noopt" */
756214117Sjamie	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
757214117Sjamie		return (1);
758214117Sjamie	while ((p = strchr(opt1, '.')) != NULL &&
759214117Sjamie	    !strncmp(opt1, opt2, ++p - opt1)) {
760214117Sjamie		opt2 += p - opt1;
761214117Sjamie		opt1 = p;
762214117Sjamie		/* "foo.noopt" vs. "foo.opt" */
763214117Sjamie		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
764214117Sjamie			return (1);
765214117Sjamie		/* "foo.opt" vs. "foo.noopt" */
766214117Sjamie		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
767214117Sjamie			return (1);
768214117Sjamie	}
769214117Sjamie	return (0);
770214117Sjamie}
771214117Sjamie
772214117Sjamie/*
773214117Sjamie * See if a jail name matches a wildcard.
774214117Sjamie */
775214117Sjamieint
776214117Sjamiewild_jail_match(const char *jname, const char *wname)
777214117Sjamie{
778214117Sjamie	const char *jc, *jd, *wc, *wd;
779214117Sjamie
780214117Sjamie	/*
781214117Sjamie	 * A non-final "*" component in the wild name matches a single jail
782214117Sjamie	 * component, and a final "*" matches one or more jail components.
783214117Sjamie	 */
784214117Sjamie	for (jc = jname, wc = wname;
785214117Sjamie	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
786214117Sjamie	     jc = jd + 1, wc = wd + 1)
787214117Sjamie		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
788214117Sjamie			return 0;
789214117Sjamie	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
790214117Sjamie}
791214117Sjamie
792214117Sjamie/*
793214117Sjamie * Return if a jail name is a wildcard.
794214117Sjamie */
795214117Sjamieint
796214117Sjamiewild_jail_name(const char *wname)
797214117Sjamie{
798214117Sjamie	const char *wc;
799214117Sjamie
800214117Sjamie	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
801214117Sjamie		if ((wc == wname || wc[-1] == '.') &&
802214117Sjamie		    (wc[1] == '\0' || wc[1] == '.'))
803214117Sjamie			return 1;
804214117Sjamie	return 0;
805214117Sjamie}
806214117Sjamie
807214117Sjamie/*
808214117Sjamie * Free a parameter record and all its strings and variables.
809214117Sjamie */
810214117Sjamiestatic void
811214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p)
812214117Sjamie{
813214117Sjamie	free(p->name);
814214117Sjamie	free_param_strings(p);
815214117Sjamie	TAILQ_REMOVE(pp, p, tq);
816214117Sjamie	free(p);
817214117Sjamie}
818214117Sjamie
819214117Sjamiestatic void
820214117Sjamiefree_param_strings(struct cfparam *p)
821214117Sjamie{
822214117Sjamie	struct cfstring *s;
823214117Sjamie	struct cfvar *v;
824214117Sjamie
825223188Sjamie	while ((s = TAILQ_FIRST(&p->val))) {
826214117Sjamie		free(s->s);
827214117Sjamie		while ((v = STAILQ_FIRST(&s->vars))) {
828214117Sjamie			free(v->name);
829214117Sjamie			STAILQ_REMOVE_HEAD(&s->vars, tq);
830214117Sjamie			free(v);
831214117Sjamie		}
832223188Sjamie		TAILQ_REMOVE(&p->val, s, tq);
833214117Sjamie		free(s);
834214117Sjamie	}
835214117Sjamie}
836