config.c revision 234988
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: head/usr.sbin/jail/config.c 234988 2012-05-03 21:39:23Z jamie $");
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
55214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
56214117Sjamie
57214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p);
58214117Sjamiestatic void free_param_strings(struct cfparam *p);
59214117Sjamie
60214117Sjamiestatic const struct ipspec intparams[] = {
61214423Sjamie    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
62214423Sjamie    [IP_COMMAND] =		{"command",		PF_INTERNAL},
63214423Sjamie    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
64214423Sjamie    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
65214423Sjamie    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
66214423Sjamie    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
67214423Sjamie    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
68214423Sjamie    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
69214423Sjamie    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
70214423Sjamie    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
71214423Sjamie    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
72214423Sjamie    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
73214423Sjamie    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
74214423Sjamie    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
75214423Sjamie							PF_INTERNAL | PF_BOOL},
76214423Sjamie    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
77214423Sjamie    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
78223351Sjamie#if defined(INET) || defined(INET6)
79214423Sjamie    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
80214423Sjamie    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
81223351Sjamie#endif
82214423Sjamie    [IP_MOUNT] =		{"mount",		PF_INTERNAL},
83214423Sjamie    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
84214423Sjamie    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
85214423Sjamie    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
86214423Sjamie    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
87223351Sjamie#ifdef INET
88214423Sjamie    [IP__IP4_IFADDR] =		{"ip4.addr",		PF_INTERNAL | PF_CONV},
89223351Sjamie#endif
90214117Sjamie#ifdef INET6
91214423Sjamie    [IP__IP6_IFADDR] =		{"ip6.addr",		PF_INTERNAL | PF_CONV},
92214117Sjamie#endif
93214783Sjamie    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",		PF_INTERNAL | PF_CONV},
94223189Sjamie    [IP__OP] =			{NULL,			PF_CONV},
95214423Sjamie    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
96214423Sjamie    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
97214423Sjamie    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
98214423Sjamie    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
99214423Sjamie    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
100214423Sjamie    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
101232242Sjamie    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
102214423Sjamie    [KP_ENFORCE_STATFS] =	{"enforce_statfs",	0},
103214423Sjamie    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
104223351Sjamie#ifdef INET
105214423Sjamie    [KP_IP4_ADDR] =		{"ip4.addr",		0},
106223351Sjamie#endif
107214423Sjamie#ifdef INET6
108214423Sjamie    [KP_IP6_ADDR] =		{"ip6.addr",		0},
109214423Sjamie#endif
110214423Sjamie    [KP_JID] =			{"jid",			0},
111214423Sjamie    [KP_NAME] =			{"name",		0},
112214423Sjamie    [KP_PATH] =			{"path",		0},
113214423Sjamie    [KP_PERSIST] =		{"persist",		0},
114214423Sjamie    [KP_SECURELEVEL] =		{"securelevel",		0},
115214423Sjamie    [KP_VNET] =			{"vnet",		0},
116214117Sjamie};
117214117Sjamie
118214117Sjamie/*
119214117Sjamie * Parse the jail configuration file.
120214117Sjamie */
121214117Sjamievoid
122214117Sjamieload_config(void)
123214117Sjamie{
124214117Sjamie	struct cfjails wild;
125214117Sjamie	struct cfparams opp;
126214117Sjamie	struct cfjail *j, *tj, *wj;
127214117Sjamie	struct cfparam *p, *vp, *tp;
128214117Sjamie	struct cfstring *s, *vs, *ns;
129214117Sjamie	struct cfvar *v;
130214117Sjamie	char *ep;
131214117Sjamie	size_t varoff;
132214117Sjamie	int did_self, jseq, pgen;
133214117Sjamie
134214117Sjamie	if (!strcmp(cfname, "-")) {
135214117Sjamie		cfname = "STDIN";
136214117Sjamie		yyin = stdin;
137214117Sjamie	} else {
138214117Sjamie		yyin = fopen(cfname, "r");
139214117Sjamie		if (!yyin)
140214117Sjamie			err(1, "%s", cfname);
141214117Sjamie	}
142214117Sjamie	if (yyparse() || yynerrs)
143214117Sjamie		exit(1);
144214117Sjamie
145214117Sjamie	/* Separate the wildcard jails out from the actual jails. */
146214117Sjamie	jseq = 0;
147214117Sjamie	TAILQ_INIT(&wild);
148214117Sjamie	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
149214117Sjamie		j->seq = ++jseq;
150214117Sjamie		if (wild_jail_name(j->name))
151214117Sjamie			requeue(j, &wild);
152214117Sjamie	}
153214117Sjamie
154214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq) {
155214117Sjamie		/* Set aside the jail's parameters. */
156214117Sjamie		TAILQ_INIT(&opp);
157214117Sjamie		TAILQ_CONCAT(&opp, &j->params, tq);
158214117Sjamie		/*
159214117Sjamie		 * The jail name implies its "name" or "jid" parameter,
160214117Sjamie		 * though they may also be explicitly set later on.
161214117Sjamie		 */
162214117Sjamie		add_param(j, NULL,
163214423Sjamie		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
164214117Sjamie		    j->name);
165214117Sjamie		/*
166214117Sjamie		 * Collect parameters for the jail, global parameters/variables,
167214117Sjamie		 * and any matching wildcard jails.
168214117Sjamie		 */
169214117Sjamie		did_self = 0;
170214117Sjamie		TAILQ_FOREACH(wj, &wild, tq) {
171214117Sjamie			if (j->seq < wj->seq && !did_self) {
172214117Sjamie				TAILQ_FOREACH(p, &opp, tq)
173214423Sjamie					add_param(j, p, 0, NULL);
174214117Sjamie				did_self = 1;
175214117Sjamie			}
176214117Sjamie			if (wild_jail_match(j->name, wj->name))
177214117Sjamie				TAILQ_FOREACH(p, &wj->params, tq)
178214423Sjamie					add_param(j, p, 0, NULL);
179214117Sjamie		}
180214117Sjamie		if (!did_self)
181214117Sjamie			TAILQ_FOREACH(p, &opp, tq)
182214423Sjamie				add_param(j, p, 0, NULL);
183214117Sjamie
184214117Sjamie		/* Resolve any variable substitutions. */
185214117Sjamie		pgen = 0;
186214117Sjamie		TAILQ_FOREACH(p, &j->params, tq) {
187214117Sjamie		    p->gen = ++pgen;
188214117Sjamie		find_vars:
189223188Sjamie		    TAILQ_FOREACH(s, &p->val, tq) {
190214117Sjamie			varoff = 0;
191214117Sjamie			while ((v = STAILQ_FIRST(&s->vars))) {
192214117Sjamie				TAILQ_FOREACH(vp, &j->params, tq)
193214117Sjamie					if (!strcmp(vp->name, v->name))
194214117Sjamie						break;
195214117Sjamie				if (!vp) {
196214117Sjamie					jail_warnx(j,
197214117Sjamie					    "%s: variable \"%s\" not found",
198214117Sjamie					    p->name, v->name);
199214117Sjamie				bad_var:
200214117Sjamie					j->flags |= JF_FAILED;
201214117Sjamie					TAILQ_FOREACH(vp, &j->params, tq)
202214117Sjamie						if (vp->gen == pgen)
203214117Sjamie							vp->flags |= PF_BAD;
204214117Sjamie					goto free_var;
205214117Sjamie				}
206214117Sjamie				if (vp->flags & PF_BAD)
207214117Sjamie					goto bad_var;
208214117Sjamie				if (vp->gen == pgen) {
209214117Sjamie					jail_warnx(j, "%s: variable loop",
210214117Sjamie					    v->name);
211214117Sjamie					goto bad_var;
212214117Sjamie				}
213223188Sjamie				TAILQ_FOREACH(vs, &vp->val, tq)
214214117Sjamie					if (!STAILQ_EMPTY(&vs->vars)) {
215214117Sjamie						vp->gen = pgen;
216214117Sjamie						TAILQ_REMOVE(&j->params, vp,
217214117Sjamie						    tq);
218214117Sjamie						TAILQ_INSERT_BEFORE(p, vp, tq);
219214117Sjamie						p = vp;
220214117Sjamie						goto find_vars;
221214117Sjamie					}
222223188Sjamie				vs = TAILQ_FIRST(&vp->val);
223223188Sjamie				if (TAILQ_NEXT(vs, tq) != NULL &&
224214117Sjamie				    (s->s[0] != '\0' ||
225214117Sjamie				     STAILQ_NEXT(v, tq))) {
226214117Sjamie					jail_warnx(j, "%s: array cannot be "
227214117Sjamie					    "substituted inline",
228214117Sjamie					    p->name);
229214117Sjamie					goto bad_var;
230214117Sjamie				}
231214117Sjamie				s->s = erealloc(s->s, s->len + vs->len + 1);
232214117Sjamie				memmove(s->s + v->pos + varoff + vs->len,
233214117Sjamie				    s->s + v->pos + varoff,
234214117Sjamie				    s->len - (v->pos + varoff) + 1);
235214117Sjamie				memcpy(s->s + v->pos + varoff, vs->s, vs->len);
236214117Sjamie				varoff += vs->len;
237214117Sjamie				s->len += vs->len;
238223188Sjamie				while ((vs = TAILQ_NEXT(vs, tq))) {
239214117Sjamie					ns = emalloc(sizeof(struct cfstring));
240214117Sjamie					ns->s = estrdup(vs->s);
241214117Sjamie					ns->len = vs->len;
242214117Sjamie					STAILQ_INIT(&ns->vars);
243223188Sjamie					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
244214117Sjamie					s = ns;
245214117Sjamie				}
246214117Sjamie			free_var:
247214117Sjamie				free(v->name);
248214117Sjamie				STAILQ_REMOVE_HEAD(&s->vars, tq);
249214117Sjamie				free(v);
250214117Sjamie			}
251214117Sjamie		    }
252214117Sjamie		}
253214117Sjamie
254214117Sjamie		/* Free the jail's original parameter list and any variables. */
255214117Sjamie		while ((p = TAILQ_FIRST(&opp)))
256214117Sjamie			free_param(&opp, p);
257214117Sjamie		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
258214117Sjamie			if (p->flags & PF_VAR)
259214117Sjamie				free_param(&j->params, p);
260214117Sjamie	}
261214117Sjamie	while ((wj = TAILQ_FIRST(&wild))) {
262214117Sjamie		free(wj->name);
263214117Sjamie		while ((p = TAILQ_FIRST(&wj->params)))
264214117Sjamie			free_param(&wj->params, p);
265214117Sjamie		TAILQ_REMOVE(&wild, wj, tq);
266214117Sjamie	}
267214117Sjamie}
268214117Sjamie
269214117Sjamie/*
270214117Sjamie * Create a new jail record.
271214117Sjamie */
272214117Sjamiestruct cfjail *
273214117Sjamieadd_jail(void)
274214117Sjamie{
275214117Sjamie	struct cfjail *j;
276214117Sjamie
277214117Sjamie	j = emalloc(sizeof(struct cfjail));
278214117Sjamie	memset(j, 0, sizeof(struct cfjail));
279214117Sjamie	TAILQ_INIT(&j->params);
280214117Sjamie	STAILQ_INIT(&j->dep[DEP_FROM]);
281214117Sjamie	STAILQ_INIT(&j->dep[DEP_TO]);
282214117Sjamie	j->queue = &cfjails;
283214117Sjamie	TAILQ_INSERT_TAIL(&cfjails, j, tq);
284214117Sjamie	return j;
285214117Sjamie}
286214117Sjamie
287214117Sjamie/*
288214117Sjamie * Add a parameter to a jail.
289214117Sjamie */
290214117Sjamievoid
291214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
292214117Sjamie    const char *value)
293214117Sjamie{
294214117Sjamie	struct cfstrings nss;
295214117Sjamie	struct cfparam *dp, *np;
296214117Sjamie	struct cfstring *s, *ns;
297214117Sjamie	struct cfvar *v, *nv;
298214423Sjamie	const char *name;
299214423Sjamie	char *cs, *tname;
300214117Sjamie	unsigned flags;
301214117Sjamie
302214117Sjamie	if (j == NULL) {
303214117Sjamie		/* Create a single anonymous jail if one doesn't yet exist. */
304214117Sjamie		j = TAILQ_LAST(&cfjails, cfjails);
305214117Sjamie		if (j == NULL)
306214117Sjamie			j = add_jail();
307214117Sjamie	}
308223188Sjamie	TAILQ_INIT(&nss);
309214117Sjamie	if (p != NULL) {
310214117Sjamie		name = p->name;
311214117Sjamie		flags = p->flags;
312214117Sjamie		/*
313214117Sjamie		 * Make a copy of the parameter's string list,
314214117Sjamie		 * which may be freed if it's overridden later.
315214117Sjamie		 */
316223188Sjamie		TAILQ_FOREACH(s, &p->val, tq) {
317214117Sjamie			ns = emalloc(sizeof(struct cfstring));
318214117Sjamie			ns->s = estrdup(s->s);
319214117Sjamie			ns->len = s->len;
320214117Sjamie			STAILQ_INIT(&ns->vars);
321214117Sjamie			STAILQ_FOREACH(v, &s->vars, tq) {
322214117Sjamie				nv = emalloc(sizeof(struct cfvar));
323214117Sjamie				nv->name = strdup(v->name);
324214117Sjamie				nv->pos = v->pos;
325214117Sjamie				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
326214117Sjamie			}
327223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
328214117Sjamie		}
329214117Sjamie	} else {
330214117Sjamie		flags = PF_APPEND;
331234988Sjamie		if (ipnum != IP__NULL) {
332214423Sjamie			name = intparams[ipnum].name;
333214423Sjamie			flags |= intparams[ipnum].flags;
334214423Sjamie		} else if ((cs = strchr(value, '='))) {
335214423Sjamie			tname = alloca(cs - value + 1);
336214423Sjamie			strlcpy(tname, value, cs - value + 1);
337214423Sjamie			name = tname;
338214423Sjamie			value = cs + 1;
339214423Sjamie		} else {
340214423Sjamie			name = value;
341214423Sjamie			value = NULL;
342214423Sjamie		}
343214117Sjamie		if (value != NULL) {
344214117Sjamie			ns = emalloc(sizeof(struct cfstring));
345214117Sjamie			ns->s = estrdup(value);
346214117Sjamie			ns->len = strlen(value);
347214117Sjamie			STAILQ_INIT(&ns->vars);
348223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
349214117Sjamie		}
350214117Sjamie	}
351214117Sjamie
352214117Sjamie	/* See if this parameter has already been added. */
353234988Sjamie	if (ipnum != IP__NULL)
354214423Sjamie		dp = j->intparams[ipnum];
355214423Sjamie	else
356214423Sjamie		TAILQ_FOREACH(dp, &j->params, tq)
357214423Sjamie			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
358214423Sjamie				break;
359214423Sjamie	if (dp != NULL) {
360214423Sjamie		/* Found it - append or replace. */
361214423Sjamie		if (strcmp(dp->name, name)) {
362214423Sjamie			free(dp->name);
363214423Sjamie			dp->name = estrdup(name);
364214117Sjamie		}
365223188Sjamie		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
366214423Sjamie			free_param_strings(dp);
367223188Sjamie		TAILQ_CONCAT(&dp->val, &nss, tq);
368214423Sjamie		dp->flags |= flags;
369214423Sjamie	} else {
370214117Sjamie		/* Not found - add it. */
371214117Sjamie		np = emalloc(sizeof(struct cfparam));
372214117Sjamie		np->name = estrdup(name);
373223188Sjamie		TAILQ_INIT(&np->val);
374223188Sjamie		TAILQ_CONCAT(&np->val, &nss, tq);
375214117Sjamie		np->flags = flags;
376214117Sjamie		np->gen = 0;
377214117Sjamie		TAILQ_INSERT_TAIL(&j->params, np, tq);
378234988Sjamie		if (ipnum != IP__NULL)
379214423Sjamie			j->intparams[ipnum] = np;
380214423Sjamie		else
381234988Sjamie			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
382214423Sjamie				if (!(intparams[ipnum].flags & PF_CONV) &&
383214423Sjamie				    equalopts(name, intparams[ipnum].name)) {
384214423Sjamie					j->intparams[ipnum] = np;
385214423Sjamie					np->flags |= intparams[ipnum].flags;
386214423Sjamie					break;
387214423Sjamie				}
388214117Sjamie	}
389214117Sjamie}
390214117Sjamie
391214117Sjamie/*
392214117Sjamie * Return if a boolean parameter exists and is true.
393214117Sjamie */
394214117Sjamieint
395214117Sjamiebool_param(const struct cfparam *p)
396214117Sjamie{
397214117Sjamie	const char *cs;
398214117Sjamie
399214117Sjamie	if (p == NULL)
400214117Sjamie		return 0;
401214117Sjamie	cs = strrchr(p->name, '.');
402214117Sjamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
403223188Sjamie	    (TAILQ_EMPTY(&p->val) ||
404223188Sjamie	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
405223188Sjamie	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
406214117Sjamie}
407214117Sjamie
408214117Sjamie/*
409214117Sjamie * Set an integer if a parameter if it exists.
410214117Sjamie */
411214117Sjamieint
412214117Sjamieint_param(const struct cfparam *p, int *ip)
413214117Sjamie{
414223188Sjamie	if (p == NULL || TAILQ_EMPTY(&p->val))
415214117Sjamie		return 0;
416223188Sjamie	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
417214117Sjamie	return 1;
418214117Sjamie}
419214117Sjamie
420214117Sjamie/*
421214117Sjamie * Return the string value of a scalar parameter if it exists.
422214117Sjamie */
423214117Sjamieconst char *
424214117Sjamiestring_param(const struct cfparam *p)
425214117Sjamie{
426223188Sjamie	return (p && !TAILQ_EMPTY(&p->val)
427223188Sjamie	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
428214117Sjamie}
429214117Sjamie
430214117Sjamie/*
431214649Sjamie * Check syntax and values of internal parameters.  Set some internal
432214649Sjamie * parameters based on the values of others.
433214117Sjamie */
434214117Sjamieint
435214649Sjamiecheck_intparams(struct cfjail *j)
436214117Sjamie{
437214649Sjamie	struct cfparam *p;
438223327Sjamie	struct cfstring *s;
439214783Sjamie	FILE *f;
440223351Sjamie	const char *val;
441214783Sjamie	char *cs, *ep, *ln;
442223351Sjamie	size_t lnlen;
443223351Sjamie	int error;
444223351Sjamie#if defined(INET) || defined(INET6)
445223351Sjamie	struct addrinfo hints;
446223351Sjamie	struct addrinfo *ai0, *ai;
447223351Sjamie	const char *hostname;
448223351Sjamie	int gicode, defif, prefix;
449223351Sjamie#endif
450223351Sjamie#ifdef INET
451223351Sjamie	struct in_addr addr4;
452223351Sjamie	int ip4ok;
453214117Sjamie	char avalue4[INET_ADDRSTRLEN];
454223351Sjamie#endif
455214117Sjamie#ifdef INET6
456214117Sjamie	struct in6_addr addr6;
457223351Sjamie	int ip6ok;
458214117Sjamie	char avalue6[INET6_ADDRSTRLEN];
459214117Sjamie#endif
460214117Sjamie
461214117Sjamie	error = 0;
462214649Sjamie	/* Check format of boolan and integer values. */
463214649Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
464223188Sjamie		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
465223188Sjamie			val = TAILQ_LAST(&p->val, cfstrings)->s;
466214649Sjamie			if (p->flags & PF_BOOL) {
467214649Sjamie				if (strcasecmp(val, "false") &&
468214649Sjamie				    strcasecmp(val, "true") &&
469214649Sjamie				    ((void)strtol(val, &ep, 10), *ep)) {
470214649Sjamie					jail_warnx(j,
471214649Sjamie					    "%s: unknown boolean value \"%s\"",
472214649Sjamie					    p->name, val);
473214649Sjamie					error = -1;
474214649Sjamie				}
475214649Sjamie			} else {
476214649Sjamie				(void)strtol(val, &ep, 10);
477214649Sjamie				if (ep == val || *ep) {
478214649Sjamie					jail_warnx(j,
479214649Sjamie					    "%s: non-integer value \"%s\"",
480214649Sjamie					    p->name, val);
481214649Sjamie					error = -1;
482214649Sjamie				}
483214649Sjamie			}
484214649Sjamie		}
485214649Sjamie	}
486214649Sjamie
487223351Sjamie#if defined(INET) || defined(INET6)
488214117Sjamie	/*
489214117Sjamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
490214117Sjamie	 * for any IP addresses it finds.
491214117Sjamie	 */
492214649Sjamie	if (((j->flags & JF_OP_MASK) != JF_STOP ||
493214649Sjamie	    j->intparams[IP_INTERFACE] != NULL) &&
494214649Sjamie	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
495214423Sjamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
496214117Sjamie		j->intparams[IP_IP_HOSTNAME] = NULL;
497214117Sjamie		/*
498214117Sjamie		 * Silently ignore unsupported address families from
499214117Sjamie		 * DNS lookups.
500214117Sjamie		 */
501223351Sjamie#ifdef INET
502223351Sjamie		ip4ok = feature_present("inet");
503214117Sjamie#endif
504214117Sjamie#ifdef INET6
505223351Sjamie		ip6ok = feature_present("inet6");
506214117Sjamie#endif
507223351Sjamie		if (
508223351Sjamie#if defined(INET) && defined(INET6)
509223351Sjamie		    ip4ok || ip6ok
510223351Sjamie#elif defined(INET)
511223351Sjamie		    ip4ok
512223351Sjamie#elif defined(INET6)
513223351Sjamie		    ip6ok
514223351Sjamie#endif
515223351Sjamie			 ) {
516214117Sjamie			/* Look up the hostname (or get the address) */
517214117Sjamie			memset(&hints, 0, sizeof(hints));
518214117Sjamie			hints.ai_socktype = SOCK_STREAM;
519214117Sjamie			hints.ai_family =
520223351Sjamie#if defined(INET) && defined(INET6)
521223351Sjamie			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
522223351Sjamie#elif defined(INET)
523223351Sjamie			    PF_INET;
524223351Sjamie#elif defined(INET6)
525223351Sjamie			    PF_INET6;
526214117Sjamie#endif
527214649Sjamie			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
528214649Sjamie			if (gicode != 0) {
529214117Sjamie				jail_warnx(j, "host.hostname %s: %s", hostname,
530214649Sjamie				    gai_strerror(gicode));
531214117Sjamie				error = -1;
532214117Sjamie			} else {
533214117Sjamie				/*
534214117Sjamie				 * Convert the addresses to ASCII so jailparam
535214117Sjamie				 * can convert them back.  Errors are not
536214117Sjamie				 * expected here.
537214117Sjamie				 */
538214117Sjamie				for (ai = ai0; ai; ai = ai->ai_next)
539214117Sjamie					switch (ai->ai_family) {
540223351Sjamie#ifdef INET
541214117Sjamie					case AF_INET:
542214117Sjamie						memcpy(&addr4,
543214117Sjamie						    &((struct sockaddr_in *)
544214117Sjamie						    (void *)ai->ai_addr)->
545214117Sjamie						    sin_addr, sizeof(addr4));
546214117Sjamie						if (inet_ntop(AF_INET,
547214117Sjamie						    &addr4, avalue4,
548214117Sjamie						    INET_ADDRSTRLEN) == NULL)
549214117Sjamie							err(1, "inet_ntop");
550214423Sjamie						add_param(j, NULL, KP_IP4_ADDR,
551214117Sjamie						    avalue4);
552214117Sjamie						break;
553223351Sjamie#endif
554214117Sjamie#ifdef INET6
555214117Sjamie					case AF_INET6:
556214117Sjamie						memcpy(&addr6,
557214117Sjamie						    &((struct sockaddr_in6 *)
558214117Sjamie						    (void *)ai->ai_addr)->
559214117Sjamie						    sin6_addr, sizeof(addr6));
560214117Sjamie						if (inet_ntop(AF_INET6,
561214117Sjamie						    &addr6, avalue6,
562214117Sjamie						    INET6_ADDRSTRLEN) == NULL)
563214117Sjamie							err(1, "inet_ntop");
564214423Sjamie						add_param(j, NULL, KP_IP6_ADDR,
565214117Sjamie						    avalue6);
566214117Sjamie						break;
567214117Sjamie#endif
568214117Sjamie					}
569214117Sjamie				freeaddrinfo(ai0);
570214117Sjamie			}
571214117Sjamie		}
572214117Sjamie	}
573214649Sjamie
574214117Sjamie	/*
575214117Sjamie	 * IP addresses may include an interface to set that address on,
576214117Sjamie	 * and a netmask/suffix for that address.
577214117Sjamie	 */
578214117Sjamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
579223351Sjamie#ifdef INET
580223351Sjamie	if (j->intparams[KP_IP4_ADDR] != NULL) {
581223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
582214117Sjamie			cs = strchr(s->s, '|');
583214423Sjamie			if (cs || defif)
584223351Sjamie				add_param(j, NULL, IP__IP4_IFADDR, s->s);
585214423Sjamie			if (cs) {
586214423Sjamie				strcpy(s->s, cs + 1);
587214423Sjamie				s->len -= cs + 1 - s->s;
588214117Sjamie			}
589214117Sjamie			if ((cs = strchr(s->s, '/'))) {
590214117Sjamie				prefix = strtol(cs + 1, &ep, 10);
591223351Sjamie				if (*ep == '.'
592223351Sjamie				    ? inet_pton(AF_INET, cs + 1, &addr4) != 1
593223351Sjamie				    : *ep || prefix < 0 || prefix > 32) {
594223351Sjamie					jail_warnx(j,
595223351Sjamie					    "ip4.addr: bad netmask \"%s\"", cs);
596223351Sjamie					error = -1;
597223351Sjamie				}
598223351Sjamie				*cs = '\0';
599223351Sjamie				s->len = cs - s->s + 1;
600223351Sjamie			}
601223351Sjamie		}
602223351Sjamie	}
603214433Sjamie#endif
604214433Sjamie#ifdef INET6
605223351Sjamie	if (j->intparams[KP_IP6_ADDR] != NULL) {
606223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
607223351Sjamie			cs = strchr(s->s, '|');
608223351Sjamie			if (cs || defif)
609223351Sjamie				add_param(j, NULL, IP__IP6_IFADDR, s->s);
610223351Sjamie			if (cs) {
611223351Sjamie				strcpy(s->s, cs + 1);
612223351Sjamie				s->len -= cs + 1 - s->s;
613223351Sjamie			}
614223351Sjamie			if ((cs = strchr(s->s, '/'))) {
615223351Sjamie				prefix = strtol(cs + 1, &ep, 10);
616223351Sjamie				if (*ep || prefix < 0 || prefix > 128) {
617214433Sjamie					jail_warnx(j,
618223351Sjamie					    "ip6.addr: bad prefixlen \"%s\"",
619214117Sjamie					    cs);
620214117Sjamie					error = -1;
621214117Sjamie				}
622214117Sjamie				*cs = '\0';
623214117Sjamie				s->len = cs - s->s + 1;
624214117Sjamie			}
625214117Sjamie		}
626214117Sjamie	}
627214117Sjamie#endif
628223351Sjamie#endif
629214783Sjamie
630214783Sjamie	/*
631214783Sjamie	 * Read mount.fstab file(s), and treat each line as its own mount
632214783Sjamie	 * parameter.
633214783Sjamie	 */
634214783Sjamie	if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
635223188Sjamie		TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
636214783Sjamie			if (s->len == 0)
637214783Sjamie				continue;
638214783Sjamie			f = fopen(s->s, "r");
639214783Sjamie			if (f == NULL) {
640214783Sjamie				jail_warnx(j, "mount.fstab: %s: %s",
641214783Sjamie				    s->s, strerror(errno));
642214783Sjamie				error = -1;
643214783Sjamie				continue;
644214783Sjamie			}
645214783Sjamie			while ((ln = fgetln(f, &lnlen))) {
646214783Sjamie				if ((cs = memchr(ln, '#', lnlen - 1)))
647214783Sjamie					lnlen = cs - ln + 1;
648214783Sjamie				if (ln[lnlen - 1] == '\n' ||
649214783Sjamie				    ln[lnlen - 1] == '#')
650214783Sjamie					ln[lnlen - 1] = '\0';
651214783Sjamie				else {
652214783Sjamie					cs = alloca(lnlen + 1);
653214783Sjamie					strlcpy(cs, ln, lnlen + 1);
654214783Sjamie					ln = cs;
655214783Sjamie				}
656214783Sjamie				add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
657214783Sjamie			}
658214783Sjamie			fclose(f);
659214783Sjamie		}
660214783Sjamie	}
661214783Sjamie	if (error)
662214783Sjamie		failed(j);
663214117Sjamie	return error;
664214117Sjamie}
665214117Sjamie
666214117Sjamie/*
667214117Sjamie * Import parameters into libjail's binary jailparam format.
668214117Sjamie */
669214117Sjamieint
670214117Sjamieimport_params(struct cfjail *j)
671214117Sjamie{
672214117Sjamie	struct cfparam *p;
673214117Sjamie	struct cfstring *s, *ts;
674214117Sjamie	struct jailparam *jp;
675214117Sjamie	char *value, *cs;
676214117Sjamie	size_t vallen;
677214117Sjamie	int error;
678214117Sjamie
679214117Sjamie	error = 0;
680214117Sjamie	j->njp = 0;
681214117Sjamie	TAILQ_FOREACH(p, &j->params, tq)
682214117Sjamie		if (!(p->flags & PF_INTERNAL))
683214117Sjamie			j->njp++;
684214117Sjamie	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
685214117Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
686214117Sjamie		if (p->flags & PF_INTERNAL)
687214117Sjamie			continue;
688214117Sjamie		if (jailparam_init(jp, p->name) < 0) {
689214117Sjamie			error = -1;
690214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
691214117Sjamie			continue;
692214117Sjamie		}
693223188Sjamie		if (TAILQ_EMPTY(&p->val))
694214117Sjamie			value = NULL;
695214117Sjamie		else if (!jp->jp_elemlen ||
696223188Sjamie			 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
697214117Sjamie			/*
698214117Sjamie			 * Scalar parameters silently discard multiple (array)
699214117Sjamie			 * values, keeping only the last value added.  This
700214117Sjamie			 * lets values added from the command line append to
701214117Sjamie			 * arrays wthout pre-checking the type.
702214117Sjamie			 */
703223188Sjamie			value = TAILQ_LAST(&p->val, cfstrings)->s;
704214117Sjamie		} else {
705214117Sjamie			/*
706214117Sjamie			 * Convert arrays into comma-separated strings, which
707214117Sjamie			 * jailparam_import will then convert back into arrays.
708214117Sjamie			 */
709214117Sjamie			vallen = 0;
710223188Sjamie			TAILQ_FOREACH(s, &p->val, tq)
711214117Sjamie				vallen += s->len + 1;
712214117Sjamie			value = alloca(vallen);
713214117Sjamie			cs = value;
714223188Sjamie			TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
715214117Sjamie				strcpy(cs, s->s);
716214117Sjamie				if (ts != NULL) {
717214117Sjamie					cs += s->len + 1;
718214117Sjamie					cs[-1] = ',';
719214117Sjamie				}
720214117Sjamie			}
721214117Sjamie		}
722214117Sjamie		if (jailparam_import(jp, value) < 0) {
723214117Sjamie			error = -1;
724214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
725214117Sjamie		}
726214117Sjamie		jp++;
727214117Sjamie	}
728214117Sjamie	if (error) {
729214117Sjamie		jailparam_free(j->jp, j->njp);
730214117Sjamie		free(j->jp);
731214117Sjamie		j->jp = NULL;
732214117Sjamie		failed(j);
733214117Sjamie	}
734214117Sjamie	return error;
735214117Sjamie}
736214117Sjamie
737214117Sjamie/*
738214117Sjamie * Check if options are equal (with or without the "no" prefix).
739214117Sjamie */
740214117Sjamieint
741214117Sjamieequalopts(const char *opt1, const char *opt2)
742214117Sjamie{
743214117Sjamie	char *p;
744214117Sjamie
745214117Sjamie	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
746214117Sjamie	if (strcmp(opt1, opt2) == 0)
747214117Sjamie		return (1);
748214117Sjamie	/* "noopt" vs. "opt" */
749214117Sjamie	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
750214117Sjamie		return (1);
751214117Sjamie	/* "opt" vs. "noopt" */
752214117Sjamie	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
753214117Sjamie		return (1);
754214117Sjamie	while ((p = strchr(opt1, '.')) != NULL &&
755214117Sjamie	    !strncmp(opt1, opt2, ++p - opt1)) {
756214117Sjamie		opt2 += p - opt1;
757214117Sjamie		opt1 = p;
758214117Sjamie		/* "foo.noopt" vs. "foo.opt" */
759214117Sjamie		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
760214117Sjamie			return (1);
761214117Sjamie		/* "foo.opt" vs. "foo.noopt" */
762214117Sjamie		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
763214117Sjamie			return (1);
764214117Sjamie	}
765214117Sjamie	return (0);
766214117Sjamie}
767214117Sjamie
768214117Sjamie/*
769214117Sjamie * See if a jail name matches a wildcard.
770214117Sjamie */
771214117Sjamieint
772214117Sjamiewild_jail_match(const char *jname, const char *wname)
773214117Sjamie{
774214117Sjamie	const char *jc, *jd, *wc, *wd;
775214117Sjamie
776214117Sjamie	/*
777214117Sjamie	 * A non-final "*" component in the wild name matches a single jail
778214117Sjamie	 * component, and a final "*" matches one or more jail components.
779214117Sjamie	 */
780214117Sjamie	for (jc = jname, wc = wname;
781214117Sjamie	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
782214117Sjamie	     jc = jd + 1, wc = wd + 1)
783214117Sjamie		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
784214117Sjamie			return 0;
785214117Sjamie	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
786214117Sjamie}
787214117Sjamie
788214117Sjamie/*
789214117Sjamie * Return if a jail name is a wildcard.
790214117Sjamie */
791214117Sjamieint
792214117Sjamiewild_jail_name(const char *wname)
793214117Sjamie{
794214117Sjamie	const char *wc;
795214117Sjamie
796214117Sjamie	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
797214117Sjamie		if ((wc == wname || wc[-1] == '.') &&
798214117Sjamie		    (wc[1] == '\0' || wc[1] == '.'))
799214117Sjamie			return 1;
800214117Sjamie	return 0;
801214117Sjamie}
802214117Sjamie
803214117Sjamie/*
804214117Sjamie * Free a parameter record and all its strings and variables.
805214117Sjamie */
806214117Sjamiestatic void
807214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p)
808214117Sjamie{
809214117Sjamie	free(p->name);
810214117Sjamie	free_param_strings(p);
811214117Sjamie	TAILQ_REMOVE(pp, p, tq);
812214117Sjamie	free(p);
813214117Sjamie}
814214117Sjamie
815214117Sjamiestatic void
816214117Sjamiefree_param_strings(struct cfparam *p)
817214117Sjamie{
818214117Sjamie	struct cfstring *s;
819214117Sjamie	struct cfvar *v;
820214117Sjamie
821223188Sjamie	while ((s = TAILQ_FIRST(&p->val))) {
822214117Sjamie		free(s->s);
823214117Sjamie		while ((v = STAILQ_FIRST(&s->vars))) {
824214117Sjamie			free(v->name);
825214117Sjamie			STAILQ_REMOVE_HEAD(&s->vars, tq);
826214117Sjamie			free(v);
827214117Sjamie		}
828223188Sjamie		TAILQ_REMOVE(&p->val, s, tq);
829214117Sjamie		free(s);
830214117Sjamie	}
831214117Sjamie}
832