config.c revision 214649
1214117Sjamie/*-
2214117Sjamie * Copyright (c) 2010 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: projects/jailconf/usr.sbin/jail/config.c 214649 2010-11-01 21:37:28Z jamie $");
29214117Sjamie
30214117Sjamie#include <sys/types.h>
31214117Sjamie#include <sys/socket.h>
32214117Sjamie#include <sys/sysctl.h>
33214117Sjamie
34214117Sjamie#include <arpa/inet.h>
35214117Sjamie#include <netinet/in.h>
36214117Sjamie
37214117Sjamie#include <err.h>
38214117Sjamie#include <netdb.h>
39214117Sjamie#include <stdio.h>
40214117Sjamie#include <stdlib.h>
41214117Sjamie#include <string.h>
42214117Sjamie
43214117Sjamie#include "jailp.h"
44214117Sjamie
45214117Sjamiestruct ipspec {
46214117Sjamie	const char	*name;
47214117Sjamie	unsigned	flags;
48214117Sjamie};
49214117Sjamie
50214117Sjamieextern FILE *yyin;
51214117Sjamieextern int yynerrs;
52214117Sjamie
53214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
54214117Sjamie
55214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p);
56214117Sjamiestatic void free_param_strings(struct cfparam *p);
57214117Sjamie
58214117Sjamiestatic const struct ipspec intparams[] = {
59214423Sjamie    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
60214423Sjamie    [IP_COMMAND] =		{"command",		PF_INTERNAL},
61214423Sjamie    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
62214423Sjamie    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
63214423Sjamie    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
64214423Sjamie    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
65214423Sjamie    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
66214423Sjamie    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
67214423Sjamie    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
68214423Sjamie    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
69214423Sjamie    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
70214423Sjamie    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
71214423Sjamie    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
72214423Sjamie    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
73214423Sjamie							PF_INTERNAL | PF_BOOL},
74214423Sjamie    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
75214423Sjamie    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
76214423Sjamie    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
77214423Sjamie    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
78214423Sjamie    [IP_MOUNT] =		{"mount",		PF_INTERNAL},
79214423Sjamie    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
80214423Sjamie    [IP_MOUNT_DEVFS_RULESET]=	{"mount.devfs.ruleset",	PF_INTERNAL},
81214423Sjamie    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
82214423Sjamie    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
83214423Sjamie    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
84214423Sjamie    [IP__IP4_IFADDR] =		{"ip4.addr",		PF_INTERNAL | PF_CONV},
85214117Sjamie#ifdef INET6
86214423Sjamie    [IP__IP6_IFADDR] =		{"ip6.addr",		PF_INTERNAL | PF_CONV},
87214117Sjamie#endif
88214423Sjamie    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
89214423Sjamie    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
90214423Sjamie    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
91214423Sjamie    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
92214423Sjamie    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
93214423Sjamie    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
94214423Sjamie    [KP_ENFORCE_STATFS] =	{"enforce_statfs",	0},
95214423Sjamie    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
96214423Sjamie    [KP_IP4_ADDR] =		{"ip4.addr",		0},
97214423Sjamie#ifdef INET6
98214423Sjamie    [KP_IP6_ADDR] =		{"ip6.addr",		0},
99214423Sjamie#endif
100214423Sjamie    [KP_JID] =			{"jid",			0},
101214423Sjamie    [KP_NAME] =			{"name",		0},
102214423Sjamie    [KP_PATH] =			{"path",		0},
103214423Sjamie    [KP_PERSIST] =		{"persist",		0},
104214423Sjamie    [KP_SECURELEVEL] =		{"securelevel",		0},
105214423Sjamie    [KP_VNET] =			{"vnet",		0},
106214117Sjamie};
107214117Sjamie
108214117Sjamie/*
109214117Sjamie * Parse the jail configuration file.
110214117Sjamie */
111214117Sjamievoid
112214117Sjamieload_config(void)
113214117Sjamie{
114214117Sjamie	struct cfjails wild;
115214117Sjamie	struct cfparams opp;
116214117Sjamie	struct cfjail *j, *tj, *wj;
117214117Sjamie	struct cfparam *p, *vp, *tp;
118214117Sjamie	struct cfstring *s, *vs, *ns;
119214117Sjamie	struct cfvar *v;
120214117Sjamie	char *ep;
121214117Sjamie	size_t varoff;
122214117Sjamie	int did_self, jseq, pgen;
123214117Sjamie
124214117Sjamie	if (!strcmp(cfname, "-")) {
125214117Sjamie		cfname = "STDIN";
126214117Sjamie		yyin = stdin;
127214117Sjamie	} else {
128214117Sjamie		yyin = fopen(cfname, "r");
129214117Sjamie		if (!yyin)
130214117Sjamie			err(1, "%s", cfname);
131214117Sjamie	}
132214117Sjamie	if (yyparse() || yynerrs)
133214117Sjamie		exit(1);
134214117Sjamie
135214117Sjamie	/* Separate the wildcard jails out from the actual jails. */
136214117Sjamie	jseq = 0;
137214117Sjamie	TAILQ_INIT(&wild);
138214117Sjamie	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
139214117Sjamie		j->seq = ++jseq;
140214117Sjamie		if (wild_jail_name(j->name))
141214117Sjamie			requeue(j, &wild);
142214117Sjamie	}
143214117Sjamie
144214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq) {
145214117Sjamie		/* Set aside the jail's parameters. */
146214117Sjamie		TAILQ_INIT(&opp);
147214117Sjamie		TAILQ_CONCAT(&opp, &j->params, tq);
148214117Sjamie		/*
149214117Sjamie		 * The jail name implies its "name" or "jid" parameter,
150214117Sjamie		 * though they may also be explicitly set later on.
151214117Sjamie		 */
152214117Sjamie		add_param(j, NULL,
153214423Sjamie		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
154214117Sjamie		    j->name);
155214117Sjamie		/*
156214117Sjamie		 * Collect parameters for the jail, global parameters/variables,
157214117Sjamie		 * and any matching wildcard jails.
158214117Sjamie		 */
159214117Sjamie		did_self = 0;
160214117Sjamie		TAILQ_FOREACH(wj, &wild, tq) {
161214117Sjamie			if (j->seq < wj->seq && !did_self) {
162214117Sjamie				TAILQ_FOREACH(p, &opp, tq)
163214423Sjamie					add_param(j, p, 0, NULL);
164214117Sjamie				did_self = 1;
165214117Sjamie			}
166214117Sjamie			if (wild_jail_match(j->name, wj->name))
167214117Sjamie				TAILQ_FOREACH(p, &wj->params, tq)
168214423Sjamie					add_param(j, p, 0, NULL);
169214117Sjamie		}
170214117Sjamie		if (!did_self)
171214117Sjamie			TAILQ_FOREACH(p, &opp, tq)
172214423Sjamie				add_param(j, p, 0, NULL);
173214117Sjamie
174214117Sjamie		/* Resolve any variable substitutions. */
175214117Sjamie		pgen = 0;
176214117Sjamie		TAILQ_FOREACH(p, &j->params, tq) {
177214117Sjamie		    p->gen = ++pgen;
178214117Sjamie		find_vars:
179214117Sjamie		    STAILQ_FOREACH(s, &p->val, tq) {
180214117Sjamie			varoff = 0;
181214117Sjamie			while ((v = STAILQ_FIRST(&s->vars))) {
182214117Sjamie				TAILQ_FOREACH(vp, &j->params, tq)
183214117Sjamie					if (!strcmp(vp->name, v->name))
184214117Sjamie						break;
185214117Sjamie				if (!vp) {
186214117Sjamie					jail_warnx(j,
187214117Sjamie					    "%s: variable \"%s\" not found",
188214117Sjamie					    p->name, v->name);
189214117Sjamie				bad_var:
190214117Sjamie					j->flags |= JF_FAILED;
191214117Sjamie					TAILQ_FOREACH(vp, &j->params, tq)
192214117Sjamie						if (vp->gen == pgen)
193214117Sjamie							vp->flags |= PF_BAD;
194214117Sjamie					goto free_var;
195214117Sjamie				}
196214117Sjamie				if (vp->flags & PF_BAD)
197214117Sjamie					goto bad_var;
198214117Sjamie				if (vp->gen == pgen) {
199214117Sjamie					jail_warnx(j, "%s: variable loop",
200214117Sjamie					    v->name);
201214117Sjamie					goto bad_var;
202214117Sjamie				}
203214117Sjamie				STAILQ_FOREACH(vs, &vp->val, tq)
204214117Sjamie					if (!STAILQ_EMPTY(&vs->vars)) {
205214117Sjamie						vp->gen = pgen;
206214117Sjamie						TAILQ_REMOVE(&j->params, vp,
207214117Sjamie						    tq);
208214117Sjamie						TAILQ_INSERT_BEFORE(p, vp, tq);
209214117Sjamie						p = vp;
210214117Sjamie						goto find_vars;
211214117Sjamie					}
212214117Sjamie				vs = STAILQ_FIRST(&vp->val);
213214117Sjamie				if (STAILQ_NEXT(vs, tq) != NULL &&
214214117Sjamie				    (s->s[0] != '\0' ||
215214117Sjamie				     STAILQ_NEXT(v, tq))) {
216214117Sjamie					jail_warnx(j, "%s: array cannot be "
217214117Sjamie					    "substituted inline",
218214117Sjamie					    p->name);
219214117Sjamie					goto bad_var;
220214117Sjamie				}
221214117Sjamie				s->s = erealloc(s->s, s->len + vs->len + 1);
222214117Sjamie				memmove(s->s + v->pos + varoff + vs->len,
223214117Sjamie				    s->s + v->pos + varoff,
224214117Sjamie				    s->len - (v->pos + varoff) + 1);
225214117Sjamie				memcpy(s->s + v->pos + varoff, vs->s, vs->len);
226214117Sjamie				varoff += vs->len;
227214117Sjamie				s->len += vs->len;
228214117Sjamie				while ((vs = STAILQ_NEXT(vs, tq))) {
229214117Sjamie					ns = emalloc(sizeof(struct cfstring));
230214117Sjamie					ns->s = estrdup(vs->s);
231214117Sjamie					ns->len = vs->len;
232214117Sjamie					STAILQ_INIT(&ns->vars);
233214117Sjamie					STAILQ_INSERT_AFTER(&p->val, s, ns, tq);
234214117Sjamie					s = ns;
235214117Sjamie				}
236214117Sjamie			free_var:
237214117Sjamie				free(v->name);
238214117Sjamie				STAILQ_REMOVE_HEAD(&s->vars, tq);
239214117Sjamie				free(v);
240214117Sjamie			}
241214117Sjamie		    }
242214117Sjamie		}
243214117Sjamie
244214117Sjamie		/* Free the jail's original parameter list and any variables. */
245214117Sjamie		while ((p = TAILQ_FIRST(&opp)))
246214117Sjamie			free_param(&opp, p);
247214117Sjamie		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
248214117Sjamie			if (p->flags & PF_VAR)
249214117Sjamie				free_param(&j->params, p);
250214117Sjamie	}
251214117Sjamie	while ((wj = TAILQ_FIRST(&wild))) {
252214117Sjamie		free(wj->name);
253214117Sjamie		while ((p = TAILQ_FIRST(&wj->params)))
254214117Sjamie			free_param(&wj->params, p);
255214117Sjamie		TAILQ_REMOVE(&wild, wj, tq);
256214117Sjamie	}
257214117Sjamie}
258214117Sjamie
259214117Sjamie/*
260214117Sjamie * Create a new jail record.
261214117Sjamie */
262214117Sjamiestruct cfjail *
263214117Sjamieadd_jail(void)
264214117Sjamie{
265214117Sjamie	struct cfjail *j;
266214117Sjamie
267214117Sjamie	j = emalloc(sizeof(struct cfjail));
268214117Sjamie	memset(j, 0, sizeof(struct cfjail));
269214117Sjamie	TAILQ_INIT(&j->params);
270214117Sjamie	STAILQ_INIT(&j->dep[DEP_FROM]);
271214117Sjamie	STAILQ_INIT(&j->dep[DEP_TO]);
272214117Sjamie	j->queue = &cfjails;
273214117Sjamie	TAILQ_INSERT_TAIL(&cfjails, j, tq);
274214117Sjamie	return j;
275214117Sjamie}
276214117Sjamie
277214117Sjamie/*
278214117Sjamie * Add a parameter to a jail.
279214117Sjamie */
280214117Sjamievoid
281214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
282214117Sjamie    const char *value)
283214117Sjamie{
284214117Sjamie	struct cfstrings nss;
285214117Sjamie	struct cfparam *dp, *np;
286214117Sjamie	struct cfstring *s, *ns;
287214117Sjamie	struct cfvar *v, *nv;
288214423Sjamie	struct ipspec *ips;
289214423Sjamie	const char *name;
290214423Sjamie	char *cs, *tname;
291214117Sjamie	unsigned flags;
292214117Sjamie
293214117Sjamie	if (j == NULL) {
294214117Sjamie		/* Create a single anonymous jail if one doesn't yet exist. */
295214117Sjamie		j = TAILQ_LAST(&cfjails, cfjails);
296214117Sjamie		if (j == NULL)
297214117Sjamie			j = add_jail();
298214117Sjamie	}
299214117Sjamie	STAILQ_INIT(&nss);
300214117Sjamie	if (p != NULL) {
301214117Sjamie		name = p->name;
302214117Sjamie		flags = p->flags;
303214117Sjamie		/*
304214117Sjamie		 * Make a copy of the parameter's string list,
305214117Sjamie		 * which may be freed if it's overridden later.
306214117Sjamie		 */
307214117Sjamie		STAILQ_FOREACH(s, &p->val, tq) {
308214117Sjamie			ns = emalloc(sizeof(struct cfstring));
309214117Sjamie			ns->s = estrdup(s->s);
310214117Sjamie			ns->len = s->len;
311214117Sjamie			STAILQ_INIT(&ns->vars);
312214117Sjamie			STAILQ_FOREACH(v, &s->vars, tq) {
313214117Sjamie				nv = emalloc(sizeof(struct cfvar));
314214117Sjamie				nv->name = strdup(v->name);
315214117Sjamie				nv->pos = v->pos;
316214117Sjamie				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
317214117Sjamie			}
318214117Sjamie			STAILQ_INSERT_TAIL(&nss, ns, tq);
319214117Sjamie		}
320214117Sjamie	} else {
321214117Sjamie		flags = PF_APPEND;
322214423Sjamie		if (ipnum != 0) {
323214423Sjamie			name = intparams[ipnum].name;
324214423Sjamie			flags |= intparams[ipnum].flags;
325214423Sjamie		} else if ((cs = strchr(value, '='))) {
326214423Sjamie			tname = alloca(cs - value + 1);
327214423Sjamie			strlcpy(tname, value, cs - value + 1);
328214423Sjamie			name = tname;
329214423Sjamie			value = cs + 1;
330214423Sjamie		} else {
331214423Sjamie			name = value;
332214423Sjamie			value = NULL;
333214423Sjamie		}
334214117Sjamie		if (value != NULL) {
335214117Sjamie			ns = emalloc(sizeof(struct cfstring));
336214117Sjamie			ns->s = estrdup(value);
337214117Sjamie			ns->len = strlen(value);
338214117Sjamie			STAILQ_INIT(&ns->vars);
339214117Sjamie			STAILQ_INSERT_TAIL(&nss, ns, tq);
340214117Sjamie		}
341214117Sjamie	}
342214117Sjamie
343214117Sjamie	/* See if this parameter has already been added. */
344214423Sjamie	if (ipnum != 0)
345214423Sjamie		dp = j->intparams[ipnum];
346214423Sjamie	else
347214423Sjamie		TAILQ_FOREACH(dp, &j->params, tq)
348214423Sjamie			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
349214423Sjamie				break;
350214423Sjamie	if (dp != NULL) {
351214423Sjamie		/* Found it - append or replace. */
352214423Sjamie		if (strcmp(dp->name, name)) {
353214423Sjamie			free(dp->name);
354214423Sjamie			dp->name = estrdup(name);
355214117Sjamie		}
356214423Sjamie		if (!(flags & PF_APPEND) || STAILQ_EMPTY(&nss))
357214423Sjamie			free_param_strings(dp);
358214423Sjamie		STAILQ_CONCAT(&dp->val, &nss);
359214423Sjamie		dp->flags |= flags;
360214423Sjamie	} else {
361214117Sjamie		/* Not found - add it. */
362214117Sjamie		np = emalloc(sizeof(struct cfparam));
363214117Sjamie		np->name = estrdup(name);
364214117Sjamie		STAILQ_INIT(&np->val);
365214117Sjamie		STAILQ_CONCAT(&np->val, &nss);
366214117Sjamie		np->flags = flags;
367214117Sjamie		np->gen = 0;
368214117Sjamie		TAILQ_INSERT_TAIL(&j->params, np, tq);
369214423Sjamie		if (ipnum != 0)
370214423Sjamie			j->intparams[ipnum] = np;
371214423Sjamie		else
372214423Sjamie			for (ipnum = 1; ipnum < IP_NPARAM; ipnum++)
373214423Sjamie				if (!(intparams[ipnum].flags & PF_CONV) &&
374214423Sjamie				    equalopts(name, intparams[ipnum].name)) {
375214423Sjamie					j->intparams[ipnum] = np;
376214423Sjamie					np->flags |= intparams[ipnum].flags;
377214423Sjamie					break;
378214423Sjamie				}
379214117Sjamie	}
380214117Sjamie}
381214117Sjamie
382214117Sjamie/*
383214117Sjamie * Return if a boolean parameter exists and is true.
384214117Sjamie */
385214117Sjamieint
386214117Sjamiebool_param(const struct cfparam *p)
387214117Sjamie{
388214117Sjamie	const char *cs;
389214117Sjamie
390214117Sjamie	if (p == NULL)
391214117Sjamie		return 0;
392214117Sjamie	cs = strrchr(p->name, '.');
393214117Sjamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
394214117Sjamie	    (STAILQ_EMPTY(&p->val) ||
395214117Sjamie	     !strcasecmp(STAILQ_LAST(&p->val, cfstring, tq)->s, "true") ||
396214117Sjamie	     (strtol(STAILQ_LAST(&p->val, cfstring, tq)->s, NULL, 10)));
397214117Sjamie}
398214117Sjamie
399214117Sjamie/*
400214117Sjamie * Set an integer if a parameter if it exists.
401214117Sjamie */
402214117Sjamieint
403214117Sjamieint_param(const struct cfparam *p, int *ip)
404214117Sjamie{
405214117Sjamie	if (p == NULL || STAILQ_EMPTY(&p->val))
406214117Sjamie		return 0;
407214117Sjamie	*ip = strtol(STAILQ_LAST(&p->val, cfstring, tq)->s, NULL, 10);
408214117Sjamie	return 1;
409214117Sjamie}
410214117Sjamie
411214117Sjamie/*
412214117Sjamie * Return the string value of a scalar parameter if it exists.
413214117Sjamie */
414214117Sjamieconst char *
415214117Sjamiestring_param(const struct cfparam *p)
416214117Sjamie{
417214117Sjamie	return (p && !STAILQ_EMPTY(&p->val)
418214117Sjamie	    ? STAILQ_LAST(&p->val, cfstring, tq)->s : NULL);
419214117Sjamie}
420214117Sjamie
421214117Sjamie/*
422214649Sjamie * Check syntax and values of internal parameters.  Set some internal
423214649Sjamie * parameters based on the values of others.
424214117Sjamie */
425214117Sjamieint
426214649Sjamiecheck_intparams(struct cfjail *j)
427214117Sjamie{
428214117Sjamie	struct in_addr addr4;
429214649Sjamie	struct addrinfo hints;
430214649Sjamie	struct addrinfo *ai0, *ai;
431214649Sjamie	struct cfparam *p;
432214117Sjamie	struct cfstring *s, *ns;
433214649Sjamie	const char *hostname, *val;
434214117Sjamie	char *cs, *ep;
435214117Sjamie	size_t size;
436214649Sjamie	int error, gicode, ip4ok, defif, prefix;
437214117Sjamie	int mib[4];
438214117Sjamie	char avalue4[INET_ADDRSTRLEN];
439214117Sjamie#ifdef INET6
440214117Sjamie	struct in6_addr addr6;
441214117Sjamie	int ip6ok, isip6;
442214117Sjamie	char avalue6[INET6_ADDRSTRLEN];
443214117Sjamie#endif
444214117Sjamie
445214117Sjamie	error = 0;
446214649Sjamie	/* Check format of boolan and integer values. */
447214649Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
448214649Sjamie		if (!STAILQ_EMPTY(&p->val) &&
449214649Sjamie		    (p->flags & (PF_BOOL | PF_INT))) {
450214649Sjamie			val = STAILQ_LAST(&p->val, cfstring, tq)->s;
451214649Sjamie			if (p->flags & PF_BOOL) {
452214649Sjamie				if (strcasecmp(val, "false") &&
453214649Sjamie				    strcasecmp(val, "true") &&
454214649Sjamie				    ((void)strtol(val, &ep, 10), *ep)) {
455214649Sjamie					jail_warnx(j,
456214649Sjamie					    "%s: unknown boolean value \"%s\"",
457214649Sjamie					    p->name, val);
458214649Sjamie					error = -1;
459214649Sjamie				}
460214649Sjamie			} else {
461214649Sjamie				(void)strtol(val, &ep, 10);
462214649Sjamie				if (ep == val || *ep) {
463214649Sjamie					jail_warnx(j,
464214649Sjamie					    "%s: non-integer value \"%s\"",
465214649Sjamie					    p->name, val);
466214649Sjamie					error = -1;
467214649Sjamie				}
468214649Sjamie			}
469214649Sjamie		}
470214649Sjamie	}
471214649Sjamie
472214117Sjamie	/*
473214117Sjamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
474214117Sjamie	 * for any IP addresses it finds.
475214117Sjamie	 */
476214649Sjamie	if (((j->flags & JF_OP_MASK) != JF_STOP ||
477214649Sjamie	    j->intparams[IP_INTERFACE] != NULL) &&
478214649Sjamie	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
479214423Sjamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
480214117Sjamie		j->intparams[IP_IP_HOSTNAME] = NULL;
481214117Sjamie		/*
482214117Sjamie		 * Silently ignore unsupported address families from
483214117Sjamie		 * DNS lookups.
484214117Sjamie		 */
485214117Sjamie		size = 4;
486214117Sjamie		ip4ok = sysctlnametomib("security.jail.param.ip4", mib, &size)
487214117Sjamie		    == 0;
488214117Sjamie#ifdef INET6
489214117Sjamie		size = 4;
490214117Sjamie		ip6ok = sysctlnametomib("security.jail.param.ip6", mib, &size)
491214117Sjamie		    == 0;
492214117Sjamie#endif
493214117Sjamie		if (ip4ok
494214117Sjamie#ifdef INET6
495214117Sjamie		    || ip6ok
496214117Sjamie#endif
497214117Sjamie			    ) {
498214117Sjamie			/* Look up the hostname (or get the address) */
499214117Sjamie			memset(&hints, 0, sizeof(hints));
500214117Sjamie			hints.ai_socktype = SOCK_STREAM;
501214117Sjamie			hints.ai_family =
502214117Sjamie#ifdef INET6
503214117Sjamie			    ip6ok ? (ip4ok ? PF_UNSPEC : PF_INET6) :
504214117Sjamie#endif
505214117Sjamie			    PF_INET;
506214649Sjamie			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
507214649Sjamie			if (gicode != 0) {
508214117Sjamie				jail_warnx(j, "host.hostname %s: %s", hostname,
509214649Sjamie				    gai_strerror(gicode));
510214117Sjamie				error = -1;
511214117Sjamie			} else {
512214117Sjamie				/*
513214117Sjamie				 * Convert the addresses to ASCII so jailparam
514214117Sjamie				 * can convert them back.  Errors are not
515214117Sjamie				 * expected here.
516214117Sjamie				 */
517214117Sjamie				for (ai = ai0; ai; ai = ai->ai_next)
518214117Sjamie					switch (ai->ai_family) {
519214117Sjamie					case AF_INET:
520214117Sjamie						memcpy(&addr4,
521214117Sjamie						    &((struct sockaddr_in *)
522214117Sjamie						    (void *)ai->ai_addr)->
523214117Sjamie						    sin_addr, sizeof(addr4));
524214117Sjamie						if (inet_ntop(AF_INET,
525214117Sjamie						    &addr4, avalue4,
526214117Sjamie						    INET_ADDRSTRLEN) == NULL)
527214117Sjamie							err(1, "inet_ntop");
528214423Sjamie						add_param(j, NULL, KP_IP4_ADDR,
529214117Sjamie						    avalue4);
530214117Sjamie						break;
531214117Sjamie#ifdef INET6
532214117Sjamie					case AF_INET6:
533214117Sjamie						memcpy(&addr6,
534214117Sjamie						    &((struct sockaddr_in6 *)
535214117Sjamie						    (void *)ai->ai_addr)->
536214117Sjamie						    sin6_addr, sizeof(addr6));
537214117Sjamie						if (inet_ntop(AF_INET6,
538214117Sjamie						    &addr6, avalue6,
539214117Sjamie						    INET6_ADDRSTRLEN) == NULL)
540214117Sjamie							err(1, "inet_ntop");
541214423Sjamie						add_param(j, NULL, KP_IP6_ADDR,
542214117Sjamie						    avalue6);
543214117Sjamie						break;
544214117Sjamie#endif
545214117Sjamie					}
546214117Sjamie				freeaddrinfo(ai0);
547214117Sjamie			}
548214117Sjamie		}
549214117Sjamie	}
550214649Sjamie
551214117Sjamie	/*
552214117Sjamie	 * IP addresses may include an interface to set that address on,
553214117Sjamie	 * and a netmask/suffix for that address.
554214117Sjamie	 */
555214117Sjamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
556214117Sjamie#ifdef INET6
557214117Sjamie	for (isip6 = 0; isip6 <= 1; isip6++)
558214117Sjamie#else
559214117Sjamie#define isip6 0
560214117Sjamie	do
561214117Sjamie#endif
562214117Sjamie	{
563214117Sjamie		if (j->intparams[KP_IP4_ADDR + isip6] == NULL)
564214117Sjamie			continue;
565214117Sjamie		STAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR + isip6]->val, tq) {
566214117Sjamie			cs = strchr(s->s, '|');
567214423Sjamie			if (cs || defif)
568214423Sjamie				add_param(j, NULL, IP__IP4_IFADDR + isip6,
569214423Sjamie				    s->s);
570214423Sjamie			if (cs) {
571214423Sjamie				strcpy(s->s, cs + 1);
572214423Sjamie				s->len -= cs + 1 - s->s;
573214117Sjamie			}
574214117Sjamie			if ((cs = strchr(s->s, '/'))) {
575214117Sjamie				prefix = strtol(cs + 1, &ep, 10);
576214433Sjamie				if (
577214433Sjamie#ifdef INET6
578214433Sjamie				    !isip6 &&
579214433Sjamie#endif
580214433Sjamie				    *ep == '.'
581214117Sjamie				    ? inet_pton(AF_INET, cs + 1, &addr4) != 1
582214433Sjamie				    : *ep || prefix < 0 || prefix > (
583214433Sjamie#ifdef INET6
584214433Sjamie				      isip6 ? 128 :
585214433Sjamie#endif
586214433Sjamie				      32)) {
587214433Sjamie					jail_warnx(j,
588214433Sjamie#ifdef INET6
589214433Sjamie					    isip6
590214433Sjamie					    ? "ip6.addr: bad prefixlen \"%s\"" :
591214433Sjamie#endif
592214433Sjamie					    "ip4.addr: bad netmask \"%s\"",
593214117Sjamie					    cs);
594214117Sjamie					error = -1;
595214117Sjamie				}
596214117Sjamie				*cs = '\0';
597214117Sjamie				s->len = cs - s->s + 1;
598214117Sjamie			}
599214117Sjamie		}
600214117Sjamie	}
601214117Sjamie#ifndef INET6
602214117Sjamie	while (0);
603214117Sjamie#endif
604214117Sjamie	return error;
605214117Sjamie}
606214117Sjamie
607214117Sjamie/*
608214117Sjamie * Import parameters into libjail's binary jailparam format.
609214117Sjamie */
610214117Sjamieint
611214117Sjamieimport_params(struct cfjail *j)
612214117Sjamie{
613214117Sjamie	struct cfparam *p;
614214117Sjamie	struct cfstring *s, *ts;
615214117Sjamie	struct jailparam *jp;
616214117Sjamie	char *value, *cs;
617214117Sjamie	size_t vallen;
618214117Sjamie	int error;
619214117Sjamie
620214117Sjamie	error = 0;
621214117Sjamie	j->njp = 0;
622214117Sjamie	TAILQ_FOREACH(p, &j->params, tq)
623214117Sjamie		if (!(p->flags & PF_INTERNAL))
624214117Sjamie			j->njp++;
625214117Sjamie	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
626214117Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
627214117Sjamie		if (p->flags & PF_INTERNAL)
628214117Sjamie			continue;
629214117Sjamie		if (jailparam_init(jp, p->name) < 0) {
630214117Sjamie			error = -1;
631214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
632214117Sjamie			continue;
633214117Sjamie		}
634214117Sjamie		if (STAILQ_EMPTY(&p->val))
635214117Sjamie			value = NULL;
636214117Sjamie		else if (!jp->jp_elemlen ||
637214117Sjamie			 !STAILQ_NEXT(STAILQ_FIRST(&p->val), tq)) {
638214117Sjamie			/*
639214117Sjamie			 * Scalar parameters silently discard multiple (array)
640214117Sjamie			 * values, keeping only the last value added.  This
641214117Sjamie			 * lets values added from the command line append to
642214117Sjamie			 * arrays wthout pre-checking the type.
643214117Sjamie			 */
644214117Sjamie			value = STAILQ_LAST(&p->val, cfstring, tq)->s;
645214117Sjamie		} else {
646214117Sjamie			/*
647214117Sjamie			 * Convert arrays into comma-separated strings, which
648214117Sjamie			 * jailparam_import will then convert back into arrays.
649214117Sjamie			 */
650214117Sjamie			vallen = 0;
651214117Sjamie			STAILQ_FOREACH(s, &p->val, tq)
652214117Sjamie				vallen += s->len + 1;
653214117Sjamie			value = alloca(vallen);
654214117Sjamie			cs = value;
655214117Sjamie			STAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
656214117Sjamie				strcpy(cs, s->s);
657214117Sjamie				if (ts != NULL) {
658214117Sjamie					cs += s->len + 1;
659214117Sjamie					cs[-1] = ',';
660214117Sjamie				}
661214117Sjamie			}
662214117Sjamie		}
663214117Sjamie		if (jailparam_import(jp, value) < 0) {
664214117Sjamie			error = -1;
665214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
666214117Sjamie		}
667214117Sjamie		jp++;
668214117Sjamie	}
669214117Sjamie	if (error) {
670214117Sjamie		jailparam_free(j->jp, j->njp);
671214117Sjamie		free(j->jp);
672214117Sjamie		j->jp = NULL;
673214117Sjamie		failed(j);
674214117Sjamie	}
675214117Sjamie	return error;
676214117Sjamie}
677214117Sjamie
678214117Sjamie/*
679214117Sjamie * Check if options are equal (with or without the "no" prefix).
680214117Sjamie */
681214117Sjamieint
682214117Sjamieequalopts(const char *opt1, const char *opt2)
683214117Sjamie{
684214117Sjamie	char *p;
685214117Sjamie
686214117Sjamie	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
687214117Sjamie	if (strcmp(opt1, opt2) == 0)
688214117Sjamie		return (1);
689214117Sjamie	/* "noopt" vs. "opt" */
690214117Sjamie	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
691214117Sjamie		return (1);
692214117Sjamie	/* "opt" vs. "noopt" */
693214117Sjamie	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
694214117Sjamie		return (1);
695214117Sjamie	while ((p = strchr(opt1, '.')) != NULL &&
696214117Sjamie	    !strncmp(opt1, opt2, ++p - opt1)) {
697214117Sjamie		opt2 += p - opt1;
698214117Sjamie		opt1 = p;
699214117Sjamie		/* "foo.noopt" vs. "foo.opt" */
700214117Sjamie		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
701214117Sjamie			return (1);
702214117Sjamie		/* "foo.opt" vs. "foo.noopt" */
703214117Sjamie		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
704214117Sjamie			return (1);
705214117Sjamie	}
706214117Sjamie	return (0);
707214117Sjamie}
708214117Sjamie
709214117Sjamie/*
710214117Sjamie * See if a jail name matches a wildcard.
711214117Sjamie */
712214117Sjamieint
713214117Sjamiewild_jail_match(const char *jname, const char *wname)
714214117Sjamie{
715214117Sjamie	const char *jc, *jd, *wc, *wd;
716214117Sjamie
717214117Sjamie	/*
718214117Sjamie	 * A non-final "*" component in the wild name matches a single jail
719214117Sjamie	 * component, and a final "*" matches one or more jail components.
720214117Sjamie	 */
721214117Sjamie	for (jc = jname, wc = wname;
722214117Sjamie	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
723214117Sjamie	     jc = jd + 1, wc = wd + 1)
724214117Sjamie		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
725214117Sjamie			return 0;
726214117Sjamie	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
727214117Sjamie}
728214117Sjamie
729214117Sjamie/*
730214117Sjamie * Return if a jail name is a wildcard.
731214117Sjamie */
732214117Sjamieint
733214117Sjamiewild_jail_name(const char *wname)
734214117Sjamie{
735214117Sjamie	const char *wc;
736214117Sjamie
737214117Sjamie	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
738214117Sjamie		if ((wc == wname || wc[-1] == '.') &&
739214117Sjamie		    (wc[1] == '\0' || wc[1] == '.'))
740214117Sjamie			return 1;
741214117Sjamie	return 0;
742214117Sjamie}
743214117Sjamie
744214117Sjamie/*
745214117Sjamie * Free a parameter record and all its strings and variables.
746214117Sjamie */
747214117Sjamiestatic void
748214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p)
749214117Sjamie{
750214117Sjamie	free(p->name);
751214117Sjamie	free_param_strings(p);
752214117Sjamie	TAILQ_REMOVE(pp, p, tq);
753214117Sjamie	free(p);
754214117Sjamie}
755214117Sjamie
756214117Sjamiestatic void
757214117Sjamiefree_param_strings(struct cfparam *p)
758214117Sjamie{
759214117Sjamie	struct cfstring *s;
760214117Sjamie	struct cfvar *v;
761214117Sjamie
762214117Sjamie	while ((s = STAILQ_FIRST(&p->val))) {
763214117Sjamie		free(s->s);
764214117Sjamie		while ((v = STAILQ_FIRST(&s->vars))) {
765214117Sjamie			free(v->name);
766214117Sjamie			STAILQ_REMOVE_HEAD(&s->vars, tq);
767214117Sjamie			free(v);
768214117Sjamie		}
769214117Sjamie		STAILQ_REMOVE_HEAD(&p->val, tq);
770214117Sjamie		free(s);
771214117Sjamie	}
772214117Sjamie}
773