config.c revision 214423
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 214423 2010-10-27 16:22:54Z 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 * Check syntax of internal parameters.
384214117Sjamie */
385214117Sjamieint
386214117Sjamiecheck_intparams(struct cfjail *j)
387214117Sjamie{
388214117Sjamie	struct cfparam *p;
389214117Sjamie	const char *val;
390214117Sjamie	char *ep;
391214117Sjamie	int error;
392214117Sjamie
393214117Sjamie	error = 0;
394214117Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
395214117Sjamie		if (!STAILQ_EMPTY(&p->val) &&
396214117Sjamie		    (p->flags & (PF_BOOL | PF_INT))) {
397214117Sjamie			val = STAILQ_LAST(&p->val, cfstring, tq)->s;
398214117Sjamie			if (p->flags & PF_BOOL) {
399214117Sjamie				if (strcasecmp(val, "false") &&
400214117Sjamie				    strcasecmp(val, "true") &&
401214117Sjamie				    ((void)strtol(val, &ep, 10), *ep)) {
402214117Sjamie					jail_warnx(j,
403214117Sjamie					    "%s: unknown boolean value \"%s\"",
404214117Sjamie					    p->name, val);
405214117Sjamie					error = -1;
406214117Sjamie				}
407214117Sjamie			} else {
408214117Sjamie				(void)strtol(val, &ep, 10);
409214117Sjamie				if (ep == val || *ep) {
410214117Sjamie					jail_warnx(j,
411214117Sjamie					    "%s: non-integer value \"%s\"",
412214117Sjamie					    p->name, val);
413214117Sjamie					error = -1;
414214117Sjamie				}
415214117Sjamie			}
416214117Sjamie		}
417214117Sjamie	}
418214117Sjamie	return error;
419214117Sjamie}
420214117Sjamie
421214117Sjamie/*
422214117Sjamie * Return if a boolean parameter exists and is true.
423214117Sjamie */
424214117Sjamieint
425214117Sjamiebool_param(const struct cfparam *p)
426214117Sjamie{
427214117Sjamie	const char *cs;
428214117Sjamie
429214117Sjamie	if (p == NULL)
430214117Sjamie		return 0;
431214117Sjamie	cs = strrchr(p->name, '.');
432214117Sjamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
433214117Sjamie	    (STAILQ_EMPTY(&p->val) ||
434214117Sjamie	     !strcasecmp(STAILQ_LAST(&p->val, cfstring, tq)->s, "true") ||
435214117Sjamie	     (strtol(STAILQ_LAST(&p->val, cfstring, tq)->s, NULL, 10)));
436214117Sjamie}
437214117Sjamie
438214117Sjamie/*
439214117Sjamie * Set an integer if a parameter if it exists.
440214117Sjamie */
441214117Sjamieint
442214117Sjamieint_param(const struct cfparam *p, int *ip)
443214117Sjamie{
444214117Sjamie	if (p == NULL || STAILQ_EMPTY(&p->val))
445214117Sjamie		return 0;
446214117Sjamie	*ip = strtol(STAILQ_LAST(&p->val, cfstring, tq)->s, NULL, 10);
447214117Sjamie	return 1;
448214117Sjamie}
449214117Sjamie
450214117Sjamie/*
451214117Sjamie * Return the string value of a scalar parameter if it exists.
452214117Sjamie */
453214117Sjamieconst char *
454214117Sjamiestring_param(const struct cfparam *p)
455214117Sjamie{
456214117Sjamie	return (p && !STAILQ_EMPTY(&p->val)
457214117Sjamie	    ? STAILQ_LAST(&p->val, cfstring, tq)->s : NULL);
458214117Sjamie}
459214117Sjamie
460214117Sjamie/*
461214117Sjamie * Look up extra IP addresses from the hostname and save interface and netmask.
462214117Sjamie */
463214117Sjamieint
464214117Sjamieip_params(struct cfjail *j)
465214117Sjamie{
466214117Sjamie	struct in_addr addr4;
467214117Sjamie	struct addrinfo hints, *ai0, *ai;
468214117Sjamie	struct cfstring *s, *ns;
469214117Sjamie	char *cs, *ep;
470214117Sjamie	const char *hostname;
471214117Sjamie	size_t size;
472214117Sjamie	int error, ip4ok, defif, prefix;
473214117Sjamie	int mib[4];
474214117Sjamie	char avalue4[INET_ADDRSTRLEN];
475214117Sjamie#ifdef INET6
476214117Sjamie	struct in6_addr addr6;
477214117Sjamie	int ip6ok, isip6;
478214117Sjamie	char avalue6[INET6_ADDRSTRLEN];
479214117Sjamie#endif
480214117Sjamie
481214117Sjamie	error = 0;
482214117Sjamie	/*
483214117Sjamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
484214117Sjamie	 * for any IP addresses it finds.
485214117Sjamie	 */
486214117Sjamie	if (bool_param(j->intparams[IP_IP_HOSTNAME]) &&
487214423Sjamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
488214117Sjamie		j->intparams[IP_IP_HOSTNAME] = NULL;
489214117Sjamie		/*
490214117Sjamie		 * Silently ignore unsupported address families from
491214117Sjamie		 * DNS lookups.
492214117Sjamie		 */
493214117Sjamie		size = 4;
494214117Sjamie		ip4ok = sysctlnametomib("security.jail.param.ip4", mib, &size)
495214117Sjamie		    == 0;
496214117Sjamie#ifdef INET6
497214117Sjamie		size = 4;
498214117Sjamie		ip6ok = sysctlnametomib("security.jail.param.ip6", mib, &size)
499214117Sjamie		    == 0;
500214117Sjamie#endif
501214117Sjamie		if (ip4ok
502214117Sjamie#ifdef INET6
503214117Sjamie		    || ip6ok
504214117Sjamie#endif
505214117Sjamie			    ) {
506214117Sjamie			/* Look up the hostname (or get the address) */
507214117Sjamie			memset(&hints, 0, sizeof(hints));
508214117Sjamie			hints.ai_socktype = SOCK_STREAM;
509214117Sjamie			hints.ai_family =
510214117Sjamie#ifdef INET6
511214117Sjamie			    ip6ok ? (ip4ok ? PF_UNSPEC : PF_INET6) :
512214117Sjamie#endif
513214117Sjamie			    PF_INET;
514214117Sjamie			error = getaddrinfo(hostname, NULL, &hints, &ai0);
515214117Sjamie			if (error != 0) {
516214117Sjamie				jail_warnx(j, "host.hostname %s: %s", hostname,
517214117Sjamie				    gai_strerror(error));
518214117Sjamie				error = -1;
519214117Sjamie			} else {
520214117Sjamie				/*
521214117Sjamie				 * Convert the addresses to ASCII so jailparam
522214117Sjamie				 * can convert them back.  Errors are not
523214117Sjamie				 * expected here.
524214117Sjamie				 */
525214117Sjamie				for (ai = ai0; ai; ai = ai->ai_next)
526214117Sjamie					switch (ai->ai_family) {
527214117Sjamie					case AF_INET:
528214117Sjamie						memcpy(&addr4,
529214117Sjamie						    &((struct sockaddr_in *)
530214117Sjamie						    (void *)ai->ai_addr)->
531214117Sjamie						    sin_addr, sizeof(addr4));
532214117Sjamie						if (inet_ntop(AF_INET,
533214117Sjamie						    &addr4, avalue4,
534214117Sjamie						    INET_ADDRSTRLEN) == NULL)
535214117Sjamie							err(1, "inet_ntop");
536214423Sjamie						add_param(j, NULL, KP_IP4_ADDR,
537214117Sjamie						    avalue4);
538214117Sjamie						break;
539214117Sjamie#ifdef INET6
540214117Sjamie					case AF_INET6:
541214117Sjamie						memcpy(&addr6,
542214117Sjamie						    &((struct sockaddr_in6 *)
543214117Sjamie						    (void *)ai->ai_addr)->
544214117Sjamie						    sin6_addr, sizeof(addr6));
545214117Sjamie						if (inet_ntop(AF_INET6,
546214117Sjamie						    &addr6, avalue6,
547214117Sjamie						    INET6_ADDRSTRLEN) == NULL)
548214117Sjamie							err(1, "inet_ntop");
549214423Sjamie						add_param(j, NULL, KP_IP6_ADDR,
550214117Sjamie						    avalue6);
551214117Sjamie						break;
552214117Sjamie#endif
553214117Sjamie					}
554214117Sjamie				freeaddrinfo(ai0);
555214117Sjamie			}
556214117Sjamie		}
557214117Sjamie	}
558214117Sjamie	/*
559214117Sjamie	 * IP addresses may include an interface to set that address on,
560214117Sjamie	 * and a netmask/suffix for that address.
561214117Sjamie	 */
562214117Sjamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
563214117Sjamie#ifdef INET6
564214117Sjamie	for (isip6 = 0; isip6 <= 1; isip6++)
565214117Sjamie#else
566214117Sjamie#define isip6 0
567214117Sjamie	do
568214117Sjamie#endif
569214117Sjamie	{
570214117Sjamie		if (j->intparams[KP_IP4_ADDR + isip6] == NULL)
571214117Sjamie			continue;
572214117Sjamie		STAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR + isip6]->val, tq) {
573214117Sjamie			cs = strchr(s->s, '|');
574214423Sjamie			if (cs || defif)
575214423Sjamie				add_param(j, NULL, IP__IP4_IFADDR + isip6,
576214423Sjamie				    s->s);
577214423Sjamie			if (cs) {
578214423Sjamie				strcpy(s->s, cs + 1);
579214423Sjamie				s->len -= cs + 1 - s->s;
580214117Sjamie			}
581214117Sjamie			if ((cs = strchr(s->s, '/'))) {
582214117Sjamie				prefix = strtol(cs + 1, &ep, 10);
583214117Sjamie				if (!isip6 && *ep == '.'
584214117Sjamie				    ? inet_pton(AF_INET, cs + 1, &addr4) != 1
585214117Sjamie				    : *ep || prefix < 0 || prefix >
586214117Sjamie				    (isip6 ? 128 : 32)) {
587214117Sjamie					jail_warnx(j, isip6
588214117Sjamie					    ? "ip6.addr: bad prefixlen \"%s\""
589214117Sjamie					    : "ip4.addr: bad netmask \"%s\"",
590214117Sjamie					    cs);
591214117Sjamie					error = -1;
592214117Sjamie				}
593214117Sjamie				*cs = '\0';
594214117Sjamie				s->len = cs - s->s + 1;
595214117Sjamie			}
596214117Sjamie		}
597214117Sjamie	}
598214117Sjamie#ifndef INET6
599214117Sjamie	while (0);
600214117Sjamie#endif
601214117Sjamie	return error;
602214117Sjamie}
603214117Sjamie
604214117Sjamie/*
605214117Sjamie * Import parameters into libjail's binary jailparam format.
606214117Sjamie */
607214117Sjamieint
608214117Sjamieimport_params(struct cfjail *j)
609214117Sjamie{
610214117Sjamie	struct cfparam *p;
611214117Sjamie	struct cfstring *s, *ts;
612214117Sjamie	struct jailparam *jp;
613214117Sjamie	char *value, *cs;
614214117Sjamie	size_t vallen;
615214117Sjamie	int error;
616214117Sjamie
617214117Sjamie	error = 0;
618214117Sjamie	j->njp = 0;
619214117Sjamie	TAILQ_FOREACH(p, &j->params, tq)
620214117Sjamie		if (!(p->flags & PF_INTERNAL))
621214117Sjamie			j->njp++;
622214117Sjamie	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
623214117Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
624214117Sjamie		if (p->flags & PF_INTERNAL)
625214117Sjamie			continue;
626214117Sjamie		if (jailparam_init(jp, p->name) < 0) {
627214117Sjamie			error = -1;
628214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
629214117Sjamie			continue;
630214117Sjamie		}
631214117Sjamie		if (STAILQ_EMPTY(&p->val))
632214117Sjamie			value = NULL;
633214117Sjamie		else if (!jp->jp_elemlen ||
634214117Sjamie			 !STAILQ_NEXT(STAILQ_FIRST(&p->val), tq)) {
635214117Sjamie			/*
636214117Sjamie			 * Scalar parameters silently discard multiple (array)
637214117Sjamie			 * values, keeping only the last value added.  This
638214117Sjamie			 * lets values added from the command line append to
639214117Sjamie			 * arrays wthout pre-checking the type.
640214117Sjamie			 */
641214117Sjamie			value = STAILQ_LAST(&p->val, cfstring, tq)->s;
642214117Sjamie		} else {
643214117Sjamie			/*
644214117Sjamie			 * Convert arrays into comma-separated strings, which
645214117Sjamie			 * jailparam_import will then convert back into arrays.
646214117Sjamie			 */
647214117Sjamie			vallen = 0;
648214117Sjamie			STAILQ_FOREACH(s, &p->val, tq)
649214117Sjamie				vallen += s->len + 1;
650214117Sjamie			value = alloca(vallen);
651214117Sjamie			cs = value;
652214117Sjamie			STAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
653214117Sjamie				strcpy(cs, s->s);
654214117Sjamie				if (ts != NULL) {
655214117Sjamie					cs += s->len + 1;
656214117Sjamie					cs[-1] = ',';
657214117Sjamie				}
658214117Sjamie			}
659214117Sjamie		}
660214117Sjamie		if (jailparam_import(jp, value) < 0) {
661214117Sjamie			error = -1;
662214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
663214117Sjamie		}
664214117Sjamie		jp++;
665214117Sjamie	}
666214117Sjamie	if (error) {
667214117Sjamie		jailparam_free(j->jp, j->njp);
668214117Sjamie		free(j->jp);
669214117Sjamie		j->jp = NULL;
670214117Sjamie		failed(j);
671214117Sjamie	}
672214117Sjamie	return error;
673214117Sjamie}
674214117Sjamie
675214117Sjamie/*
676214117Sjamie * Check if options are equal (with or without the "no" prefix).
677214117Sjamie */
678214117Sjamieint
679214117Sjamieequalopts(const char *opt1, const char *opt2)
680214117Sjamie{
681214117Sjamie	char *p;
682214117Sjamie
683214117Sjamie	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
684214117Sjamie	if (strcmp(opt1, opt2) == 0)
685214117Sjamie		return (1);
686214117Sjamie	/* "noopt" vs. "opt" */
687214117Sjamie	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
688214117Sjamie		return (1);
689214117Sjamie	/* "opt" vs. "noopt" */
690214117Sjamie	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
691214117Sjamie		return (1);
692214117Sjamie	while ((p = strchr(opt1, '.')) != NULL &&
693214117Sjamie	    !strncmp(opt1, opt2, ++p - opt1)) {
694214117Sjamie		opt2 += p - opt1;
695214117Sjamie		opt1 = p;
696214117Sjamie		/* "foo.noopt" vs. "foo.opt" */
697214117Sjamie		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
698214117Sjamie			return (1);
699214117Sjamie		/* "foo.opt" vs. "foo.noopt" */
700214117Sjamie		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
701214117Sjamie			return (1);
702214117Sjamie	}
703214117Sjamie	return (0);
704214117Sjamie}
705214117Sjamie
706214117Sjamie/*
707214117Sjamie * See if a jail name matches a wildcard.
708214117Sjamie */
709214117Sjamieint
710214117Sjamiewild_jail_match(const char *jname, const char *wname)
711214117Sjamie{
712214117Sjamie	const char *jc, *jd, *wc, *wd;
713214117Sjamie
714214117Sjamie	/*
715214117Sjamie	 * A non-final "*" component in the wild name matches a single jail
716214117Sjamie	 * component, and a final "*" matches one or more jail components.
717214117Sjamie	 */
718214117Sjamie	for (jc = jname, wc = wname;
719214117Sjamie	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
720214117Sjamie	     jc = jd + 1, wc = wd + 1)
721214117Sjamie		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
722214117Sjamie			return 0;
723214117Sjamie	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
724214117Sjamie}
725214117Sjamie
726214117Sjamie/*
727214117Sjamie * Return if a jail name is a wildcard.
728214117Sjamie */
729214117Sjamieint
730214117Sjamiewild_jail_name(const char *wname)
731214117Sjamie{
732214117Sjamie	const char *wc;
733214117Sjamie
734214117Sjamie	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
735214117Sjamie		if ((wc == wname || wc[-1] == '.') &&
736214117Sjamie		    (wc[1] == '\0' || wc[1] == '.'))
737214117Sjamie			return 1;
738214117Sjamie	return 0;
739214117Sjamie}
740214117Sjamie
741214117Sjamie/*
742214117Sjamie * Free a parameter record and all its strings and variables.
743214117Sjamie */
744214117Sjamiestatic void
745214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p)
746214117Sjamie{
747214117Sjamie	free(p->name);
748214117Sjamie	free_param_strings(p);
749214117Sjamie	TAILQ_REMOVE(pp, p, tq);
750214117Sjamie	free(p);
751214117Sjamie}
752214117Sjamie
753214117Sjamiestatic void
754214117Sjamiefree_param_strings(struct cfparam *p)
755214117Sjamie{
756214117Sjamie	struct cfstring *s;
757214117Sjamie	struct cfvar *v;
758214117Sjamie
759214117Sjamie	while ((s = STAILQ_FIRST(&p->val))) {
760214117Sjamie		free(s->s);
761214117Sjamie		while ((v = STAILQ_FIRST(&s->vars))) {
762214117Sjamie			free(v->name);
763214117Sjamie			STAILQ_REMOVE_HEAD(&s->vars, tq);
764214117Sjamie			free(v);
765214117Sjamie		}
766214117Sjamie		STAILQ_REMOVE_HEAD(&p->val, tq);
767214117Sjamie		free(s);
768214117Sjamie	}
769214117Sjamie}
770