config.c revision 239601
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 239601 2012-08-23 01:43:01Z 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
55235789Sbaptextern int yyparse(void);
56235789Sbapt
57214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
58214117Sjamie
59214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p);
60214117Sjamiestatic void free_param_strings(struct cfparam *p);
61214117Sjamie
62214117Sjamiestatic const struct ipspec intparams[] = {
63214423Sjamie    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
64214423Sjamie    [IP_COMMAND] =		{"command",		PF_INTERNAL},
65214423Sjamie    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
66214423Sjamie    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
67214423Sjamie    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
68214423Sjamie    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
69214423Sjamie    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
70214423Sjamie    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
71214423Sjamie    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
72214423Sjamie    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
73214423Sjamie    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
74214423Sjamie    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
75214423Sjamie    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
76214423Sjamie    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
77214423Sjamie							PF_INTERNAL | PF_BOOL},
78214423Sjamie    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
79214423Sjamie    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
80223351Sjamie#if defined(INET) || defined(INET6)
81214423Sjamie    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
82214423Sjamie    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
83223351Sjamie#endif
84214423Sjamie    [IP_MOUNT] =		{"mount",		PF_INTERNAL},
85214423Sjamie    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
86214423Sjamie    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
87214423Sjamie    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
88214423Sjamie    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
89223351Sjamie#ifdef INET
90214423Sjamie    [IP__IP4_IFADDR] =		{"ip4.addr",		PF_INTERNAL | PF_CONV},
91223351Sjamie#endif
92214117Sjamie#ifdef INET6
93214423Sjamie    [IP__IP6_IFADDR] =		{"ip6.addr",		PF_INTERNAL | PF_CONV},
94214117Sjamie#endif
95214783Sjamie    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",		PF_INTERNAL | PF_CONV},
96223189Sjamie    [IP__OP] =			{NULL,			PF_CONV},
97214423Sjamie    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
98214423Sjamie    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
99214423Sjamie    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
100214423Sjamie    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
101214423Sjamie    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
102214423Sjamie    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
103232242Sjamie    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
104214423Sjamie    [KP_ENFORCE_STATFS] =	{"enforce_statfs",	0},
105214423Sjamie    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
106223351Sjamie#ifdef INET
107214423Sjamie    [KP_IP4_ADDR] =		{"ip4.addr",		0},
108223351Sjamie#endif
109214423Sjamie#ifdef INET6
110214423Sjamie    [KP_IP6_ADDR] =		{"ip6.addr",		0},
111214423Sjamie#endif
112214423Sjamie    [KP_JID] =			{"jid",			0},
113214423Sjamie    [KP_NAME] =			{"name",		0},
114214423Sjamie    [KP_PATH] =			{"path",		0},
115214423Sjamie    [KP_PERSIST] =		{"persist",		0},
116214423Sjamie    [KP_SECURELEVEL] =		{"securelevel",		0},
117214423Sjamie    [KP_VNET] =			{"vnet",		0},
118214117Sjamie};
119214117Sjamie
120214117Sjamie/*
121214117Sjamie * Parse the jail configuration file.
122214117Sjamie */
123214117Sjamievoid
124214117Sjamieload_config(void)
125214117Sjamie{
126214117Sjamie	struct cfjails wild;
127214117Sjamie	struct cfparams opp;
128214117Sjamie	struct cfjail *j, *tj, *wj;
129214117Sjamie	struct cfparam *p, *vp, *tp;
130214117Sjamie	struct cfstring *s, *vs, *ns;
131214117Sjamie	struct cfvar *v;
132214117Sjamie	char *ep;
133214117Sjamie	size_t varoff;
134214117Sjamie	int did_self, jseq, pgen;
135214117Sjamie
136214117Sjamie	if (!strcmp(cfname, "-")) {
137214117Sjamie		cfname = "STDIN";
138214117Sjamie		yyin = stdin;
139214117Sjamie	} else {
140214117Sjamie		yyin = fopen(cfname, "r");
141214117Sjamie		if (!yyin)
142214117Sjamie			err(1, "%s", cfname);
143214117Sjamie	}
144214117Sjamie	if (yyparse() || yynerrs)
145214117Sjamie		exit(1);
146214117Sjamie
147214117Sjamie	/* Separate the wildcard jails out from the actual jails. */
148214117Sjamie	jseq = 0;
149214117Sjamie	TAILQ_INIT(&wild);
150214117Sjamie	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
151214117Sjamie		j->seq = ++jseq;
152214117Sjamie		if (wild_jail_name(j->name))
153214117Sjamie			requeue(j, &wild);
154214117Sjamie	}
155214117Sjamie
156214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq) {
157214117Sjamie		/* Set aside the jail's parameters. */
158214117Sjamie		TAILQ_INIT(&opp);
159214117Sjamie		TAILQ_CONCAT(&opp, &j->params, tq);
160214117Sjamie		/*
161214117Sjamie		 * The jail name implies its "name" or "jid" parameter,
162214117Sjamie		 * though they may also be explicitly set later on.
163214117Sjamie		 */
164214117Sjamie		add_param(j, NULL,
165214423Sjamie		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
166214117Sjamie		    j->name);
167214117Sjamie		/*
168214117Sjamie		 * Collect parameters for the jail, global parameters/variables,
169214117Sjamie		 * and any matching wildcard jails.
170214117Sjamie		 */
171214117Sjamie		did_self = 0;
172214117Sjamie		TAILQ_FOREACH(wj, &wild, tq) {
173214117Sjamie			if (j->seq < wj->seq && !did_self) {
174214117Sjamie				TAILQ_FOREACH(p, &opp, tq)
175214423Sjamie					add_param(j, p, 0, NULL);
176214117Sjamie				did_self = 1;
177214117Sjamie			}
178214117Sjamie			if (wild_jail_match(j->name, wj->name))
179214117Sjamie				TAILQ_FOREACH(p, &wj->params, tq)
180214423Sjamie					add_param(j, p, 0, NULL);
181214117Sjamie		}
182214117Sjamie		if (!did_self)
183214117Sjamie			TAILQ_FOREACH(p, &opp, tq)
184214423Sjamie				add_param(j, p, 0, NULL);
185214117Sjamie
186214117Sjamie		/* Resolve any variable substitutions. */
187214117Sjamie		pgen = 0;
188214117Sjamie		TAILQ_FOREACH(p, &j->params, tq) {
189214117Sjamie		    p->gen = ++pgen;
190214117Sjamie		find_vars:
191223188Sjamie		    TAILQ_FOREACH(s, &p->val, tq) {
192214117Sjamie			varoff = 0;
193214117Sjamie			while ((v = STAILQ_FIRST(&s->vars))) {
194214117Sjamie				TAILQ_FOREACH(vp, &j->params, tq)
195214117Sjamie					if (!strcmp(vp->name, v->name))
196214117Sjamie						break;
197214117Sjamie				if (!vp) {
198214117Sjamie					jail_warnx(j,
199214117Sjamie					    "%s: variable \"%s\" not found",
200214117Sjamie					    p->name, v->name);
201214117Sjamie				bad_var:
202214117Sjamie					j->flags |= JF_FAILED;
203214117Sjamie					TAILQ_FOREACH(vp, &j->params, tq)
204214117Sjamie						if (vp->gen == pgen)
205214117Sjamie							vp->flags |= PF_BAD;
206214117Sjamie					goto free_var;
207214117Sjamie				}
208214117Sjamie				if (vp->flags & PF_BAD)
209214117Sjamie					goto bad_var;
210214117Sjamie				if (vp->gen == pgen) {
211214117Sjamie					jail_warnx(j, "%s: variable loop",
212214117Sjamie					    v->name);
213214117Sjamie					goto bad_var;
214214117Sjamie				}
215223188Sjamie				TAILQ_FOREACH(vs, &vp->val, tq)
216214117Sjamie					if (!STAILQ_EMPTY(&vs->vars)) {
217214117Sjamie						vp->gen = pgen;
218214117Sjamie						TAILQ_REMOVE(&j->params, vp,
219214117Sjamie						    tq);
220214117Sjamie						TAILQ_INSERT_BEFORE(p, vp, tq);
221214117Sjamie						p = vp;
222214117Sjamie						goto find_vars;
223214117Sjamie					}
224223188Sjamie				vs = TAILQ_FIRST(&vp->val);
225223188Sjamie				if (TAILQ_NEXT(vs, tq) != NULL &&
226214117Sjamie				    (s->s[0] != '\0' ||
227214117Sjamie				     STAILQ_NEXT(v, tq))) {
228214117Sjamie					jail_warnx(j, "%s: array cannot be "
229214117Sjamie					    "substituted inline",
230214117Sjamie					    p->name);
231214117Sjamie					goto bad_var;
232214117Sjamie				}
233214117Sjamie				s->s = erealloc(s->s, s->len + vs->len + 1);
234214117Sjamie				memmove(s->s + v->pos + varoff + vs->len,
235214117Sjamie				    s->s + v->pos + varoff,
236214117Sjamie				    s->len - (v->pos + varoff) + 1);
237214117Sjamie				memcpy(s->s + v->pos + varoff, vs->s, vs->len);
238214117Sjamie				varoff += vs->len;
239214117Sjamie				s->len += vs->len;
240223188Sjamie				while ((vs = TAILQ_NEXT(vs, tq))) {
241214117Sjamie					ns = emalloc(sizeof(struct cfstring));
242214117Sjamie					ns->s = estrdup(vs->s);
243214117Sjamie					ns->len = vs->len;
244214117Sjamie					STAILQ_INIT(&ns->vars);
245223188Sjamie					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
246214117Sjamie					s = ns;
247214117Sjamie				}
248214117Sjamie			free_var:
249214117Sjamie				free(v->name);
250214117Sjamie				STAILQ_REMOVE_HEAD(&s->vars, tq);
251214117Sjamie				free(v);
252214117Sjamie			}
253214117Sjamie		    }
254214117Sjamie		}
255214117Sjamie
256214117Sjamie		/* Free the jail's original parameter list and any variables. */
257214117Sjamie		while ((p = TAILQ_FIRST(&opp)))
258214117Sjamie			free_param(&opp, p);
259214117Sjamie		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
260214117Sjamie			if (p->flags & PF_VAR)
261214117Sjamie				free_param(&j->params, p);
262214117Sjamie	}
263214117Sjamie	while ((wj = TAILQ_FIRST(&wild))) {
264214117Sjamie		free(wj->name);
265214117Sjamie		while ((p = TAILQ_FIRST(&wj->params)))
266214117Sjamie			free_param(&wj->params, p);
267214117Sjamie		TAILQ_REMOVE(&wild, wj, tq);
268214117Sjamie	}
269214117Sjamie}
270214117Sjamie
271214117Sjamie/*
272214117Sjamie * Create a new jail record.
273214117Sjamie */
274214117Sjamiestruct cfjail *
275214117Sjamieadd_jail(void)
276214117Sjamie{
277214117Sjamie	struct cfjail *j;
278214117Sjamie
279214117Sjamie	j = emalloc(sizeof(struct cfjail));
280214117Sjamie	memset(j, 0, sizeof(struct cfjail));
281214117Sjamie	TAILQ_INIT(&j->params);
282214117Sjamie	STAILQ_INIT(&j->dep[DEP_FROM]);
283214117Sjamie	STAILQ_INIT(&j->dep[DEP_TO]);
284214117Sjamie	j->queue = &cfjails;
285214117Sjamie	TAILQ_INSERT_TAIL(&cfjails, j, tq);
286214117Sjamie	return j;
287214117Sjamie}
288214117Sjamie
289214117Sjamie/*
290214117Sjamie * Add a parameter to a jail.
291214117Sjamie */
292214117Sjamievoid
293214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
294214117Sjamie    const char *value)
295214117Sjamie{
296214117Sjamie	struct cfstrings nss;
297214117Sjamie	struct cfparam *dp, *np;
298214117Sjamie	struct cfstring *s, *ns;
299214117Sjamie	struct cfvar *v, *nv;
300214423Sjamie	const char *name;
301214423Sjamie	char *cs, *tname;
302214117Sjamie	unsigned flags;
303214117Sjamie
304214117Sjamie	if (j == NULL) {
305214117Sjamie		/* Create a single anonymous jail if one doesn't yet exist. */
306214117Sjamie		j = TAILQ_LAST(&cfjails, cfjails);
307214117Sjamie		if (j == NULL)
308214117Sjamie			j = add_jail();
309214117Sjamie	}
310223188Sjamie	TAILQ_INIT(&nss);
311214117Sjamie	if (p != NULL) {
312214117Sjamie		name = p->name;
313214117Sjamie		flags = p->flags;
314214117Sjamie		/*
315214117Sjamie		 * Make a copy of the parameter's string list,
316214117Sjamie		 * which may be freed if it's overridden later.
317214117Sjamie		 */
318223188Sjamie		TAILQ_FOREACH(s, &p->val, tq) {
319214117Sjamie			ns = emalloc(sizeof(struct cfstring));
320214117Sjamie			ns->s = estrdup(s->s);
321214117Sjamie			ns->len = s->len;
322214117Sjamie			STAILQ_INIT(&ns->vars);
323214117Sjamie			STAILQ_FOREACH(v, &s->vars, tq) {
324214117Sjamie				nv = emalloc(sizeof(struct cfvar));
325214117Sjamie				nv->name = strdup(v->name);
326214117Sjamie				nv->pos = v->pos;
327214117Sjamie				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
328214117Sjamie			}
329223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
330214117Sjamie		}
331214117Sjamie	} else {
332214117Sjamie		flags = PF_APPEND;
333234988Sjamie		if (ipnum != IP__NULL) {
334214423Sjamie			name = intparams[ipnum].name;
335214423Sjamie			flags |= intparams[ipnum].flags;
336214423Sjamie		} else if ((cs = strchr(value, '='))) {
337214423Sjamie			tname = alloca(cs - value + 1);
338214423Sjamie			strlcpy(tname, value, cs - value + 1);
339214423Sjamie			name = tname;
340214423Sjamie			value = cs + 1;
341214423Sjamie		} else {
342214423Sjamie			name = value;
343214423Sjamie			value = NULL;
344214423Sjamie		}
345214117Sjamie		if (value != NULL) {
346214117Sjamie			ns = emalloc(sizeof(struct cfstring));
347214117Sjamie			ns->s = estrdup(value);
348214117Sjamie			ns->len = strlen(value);
349214117Sjamie			STAILQ_INIT(&ns->vars);
350223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
351214117Sjamie		}
352214117Sjamie	}
353214117Sjamie
354214117Sjamie	/* See if this parameter has already been added. */
355234988Sjamie	if (ipnum != IP__NULL)
356214423Sjamie		dp = j->intparams[ipnum];
357214423Sjamie	else
358214423Sjamie		TAILQ_FOREACH(dp, &j->params, tq)
359214423Sjamie			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
360214423Sjamie				break;
361214423Sjamie	if (dp != NULL) {
362214423Sjamie		/* Found it - append or replace. */
363214423Sjamie		if (strcmp(dp->name, name)) {
364214423Sjamie			free(dp->name);
365214423Sjamie			dp->name = estrdup(name);
366214117Sjamie		}
367223188Sjamie		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
368214423Sjamie			free_param_strings(dp);
369223188Sjamie		TAILQ_CONCAT(&dp->val, &nss, tq);
370214423Sjamie		dp->flags |= flags;
371214423Sjamie	} else {
372214117Sjamie		/* Not found - add it. */
373214117Sjamie		np = emalloc(sizeof(struct cfparam));
374214117Sjamie		np->name = estrdup(name);
375223188Sjamie		TAILQ_INIT(&np->val);
376223188Sjamie		TAILQ_CONCAT(&np->val, &nss, tq);
377214117Sjamie		np->flags = flags;
378214117Sjamie		np->gen = 0;
379214117Sjamie		TAILQ_INSERT_TAIL(&j->params, np, tq);
380234988Sjamie		if (ipnum != IP__NULL)
381214423Sjamie			j->intparams[ipnum] = np;
382214423Sjamie		else
383234988Sjamie			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
384214423Sjamie				if (!(intparams[ipnum].flags & PF_CONV) &&
385214423Sjamie				    equalopts(name, intparams[ipnum].name)) {
386214423Sjamie					j->intparams[ipnum] = np;
387214423Sjamie					np->flags |= intparams[ipnum].flags;
388214423Sjamie					break;
389214423Sjamie				}
390214117Sjamie	}
391214117Sjamie}
392214117Sjamie
393214117Sjamie/*
394214117Sjamie * Return if a boolean parameter exists and is true.
395214117Sjamie */
396214117Sjamieint
397214117Sjamiebool_param(const struct cfparam *p)
398214117Sjamie{
399214117Sjamie	const char *cs;
400214117Sjamie
401214117Sjamie	if (p == NULL)
402214117Sjamie		return 0;
403214117Sjamie	cs = strrchr(p->name, '.');
404214117Sjamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
405223188Sjamie	    (TAILQ_EMPTY(&p->val) ||
406223188Sjamie	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
407223188Sjamie	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
408214117Sjamie}
409214117Sjamie
410214117Sjamie/*
411214117Sjamie * Set an integer if a parameter if it exists.
412214117Sjamie */
413214117Sjamieint
414214117Sjamieint_param(const struct cfparam *p, int *ip)
415214117Sjamie{
416223188Sjamie	if (p == NULL || TAILQ_EMPTY(&p->val))
417214117Sjamie		return 0;
418223188Sjamie	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
419214117Sjamie	return 1;
420214117Sjamie}
421214117Sjamie
422214117Sjamie/*
423214117Sjamie * Return the string value of a scalar parameter if it exists.
424214117Sjamie */
425214117Sjamieconst char *
426214117Sjamiestring_param(const struct cfparam *p)
427214117Sjamie{
428223188Sjamie	return (p && !TAILQ_EMPTY(&p->val)
429223188Sjamie	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
430214117Sjamie}
431214117Sjamie
432214117Sjamie/*
433214649Sjamie * Check syntax and values of internal parameters.  Set some internal
434214649Sjamie * parameters based on the values of others.
435214117Sjamie */
436214117Sjamieint
437214649Sjamiecheck_intparams(struct cfjail *j)
438214117Sjamie{
439214649Sjamie	struct cfparam *p;
440223327Sjamie	struct cfstring *s;
441214783Sjamie	FILE *f;
442223351Sjamie	const char *val;
443214783Sjamie	char *cs, *ep, *ln;
444223351Sjamie	size_t lnlen;
445223351Sjamie	int error;
446223351Sjamie#if defined(INET) || defined(INET6)
447223351Sjamie	struct addrinfo hints;
448223351Sjamie	struct addrinfo *ai0, *ai;
449223351Sjamie	const char *hostname;
450223351Sjamie	int gicode, defif, prefix;
451223351Sjamie#endif
452223351Sjamie#ifdef INET
453223351Sjamie	struct in_addr addr4;
454223351Sjamie	int ip4ok;
455214117Sjamie	char avalue4[INET_ADDRSTRLEN];
456223351Sjamie#endif
457214117Sjamie#ifdef INET6
458214117Sjamie	struct in6_addr addr6;
459223351Sjamie	int ip6ok;
460214117Sjamie	char avalue6[INET6_ADDRSTRLEN];
461214117Sjamie#endif
462214117Sjamie
463214117Sjamie	error = 0;
464214649Sjamie	/* Check format of boolan and integer values. */
465214649Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
466223188Sjamie		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
467223188Sjamie			val = TAILQ_LAST(&p->val, cfstrings)->s;
468214649Sjamie			if (p->flags & PF_BOOL) {
469214649Sjamie				if (strcasecmp(val, "false") &&
470214649Sjamie				    strcasecmp(val, "true") &&
471214649Sjamie				    ((void)strtol(val, &ep, 10), *ep)) {
472214649Sjamie					jail_warnx(j,
473214649Sjamie					    "%s: unknown boolean value \"%s\"",
474214649Sjamie					    p->name, val);
475214649Sjamie					error = -1;
476214649Sjamie				}
477214649Sjamie			} else {
478214649Sjamie				(void)strtol(val, &ep, 10);
479214649Sjamie				if (ep == val || *ep) {
480214649Sjamie					jail_warnx(j,
481214649Sjamie					    "%s: non-integer value \"%s\"",
482214649Sjamie					    p->name, val);
483214649Sjamie					error = -1;
484214649Sjamie				}
485214649Sjamie			}
486214649Sjamie		}
487214649Sjamie	}
488214649Sjamie
489223351Sjamie#if defined(INET) || defined(INET6)
490214117Sjamie	/*
491214117Sjamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
492214117Sjamie	 * for any IP addresses it finds.
493214117Sjamie	 */
494214649Sjamie	if (((j->flags & JF_OP_MASK) != JF_STOP ||
495214649Sjamie	    j->intparams[IP_INTERFACE] != NULL) &&
496214649Sjamie	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
497214423Sjamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
498214117Sjamie		j->intparams[IP_IP_HOSTNAME] = NULL;
499214117Sjamie		/*
500214117Sjamie		 * Silently ignore unsupported address families from
501214117Sjamie		 * DNS lookups.
502214117Sjamie		 */
503223351Sjamie#ifdef INET
504223351Sjamie		ip4ok = feature_present("inet");
505214117Sjamie#endif
506214117Sjamie#ifdef INET6
507223351Sjamie		ip6ok = feature_present("inet6");
508214117Sjamie#endif
509223351Sjamie		if (
510223351Sjamie#if defined(INET) && defined(INET6)
511223351Sjamie		    ip4ok || ip6ok
512223351Sjamie#elif defined(INET)
513223351Sjamie		    ip4ok
514223351Sjamie#elif defined(INET6)
515223351Sjamie		    ip6ok
516223351Sjamie#endif
517223351Sjamie			 ) {
518214117Sjamie			/* Look up the hostname (or get the address) */
519214117Sjamie			memset(&hints, 0, sizeof(hints));
520214117Sjamie			hints.ai_socktype = SOCK_STREAM;
521214117Sjamie			hints.ai_family =
522223351Sjamie#if defined(INET) && defined(INET6)
523223351Sjamie			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
524223351Sjamie#elif defined(INET)
525223351Sjamie			    PF_INET;
526223351Sjamie#elif defined(INET6)
527223351Sjamie			    PF_INET6;
528214117Sjamie#endif
529214649Sjamie			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
530214649Sjamie			if (gicode != 0) {
531214117Sjamie				jail_warnx(j, "host.hostname %s: %s", hostname,
532214649Sjamie				    gai_strerror(gicode));
533214117Sjamie				error = -1;
534214117Sjamie			} else {
535214117Sjamie				/*
536214117Sjamie				 * Convert the addresses to ASCII so jailparam
537214117Sjamie				 * can convert them back.  Errors are not
538214117Sjamie				 * expected here.
539214117Sjamie				 */
540214117Sjamie				for (ai = ai0; ai; ai = ai->ai_next)
541214117Sjamie					switch (ai->ai_family) {
542223351Sjamie#ifdef INET
543214117Sjamie					case AF_INET:
544214117Sjamie						memcpy(&addr4,
545214117Sjamie						    &((struct sockaddr_in *)
546214117Sjamie						    (void *)ai->ai_addr)->
547214117Sjamie						    sin_addr, sizeof(addr4));
548214117Sjamie						if (inet_ntop(AF_INET,
549214117Sjamie						    &addr4, avalue4,
550214117Sjamie						    INET_ADDRSTRLEN) == NULL)
551214117Sjamie							err(1, "inet_ntop");
552214423Sjamie						add_param(j, NULL, KP_IP4_ADDR,
553214117Sjamie						    avalue4);
554214117Sjamie						break;
555223351Sjamie#endif
556214117Sjamie#ifdef INET6
557214117Sjamie					case AF_INET6:
558214117Sjamie						memcpy(&addr6,
559214117Sjamie						    &((struct sockaddr_in6 *)
560214117Sjamie						    (void *)ai->ai_addr)->
561214117Sjamie						    sin6_addr, sizeof(addr6));
562214117Sjamie						if (inet_ntop(AF_INET6,
563214117Sjamie						    &addr6, avalue6,
564214117Sjamie						    INET6_ADDRSTRLEN) == NULL)
565214117Sjamie							err(1, "inet_ntop");
566214423Sjamie						add_param(j, NULL, KP_IP6_ADDR,
567214117Sjamie						    avalue6);
568214117Sjamie						break;
569214117Sjamie#endif
570214117Sjamie					}
571214117Sjamie				freeaddrinfo(ai0);
572214117Sjamie			}
573214117Sjamie		}
574214117Sjamie	}
575214649Sjamie
576214117Sjamie	/*
577214117Sjamie	 * IP addresses may include an interface to set that address on,
578214117Sjamie	 * and a netmask/suffix for that address.
579214117Sjamie	 */
580214117Sjamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
581223351Sjamie#ifdef INET
582223351Sjamie	if (j->intparams[KP_IP4_ADDR] != NULL) {
583223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
584214117Sjamie			cs = strchr(s->s, '|');
585214423Sjamie			if (cs || defif)
586223351Sjamie				add_param(j, NULL, IP__IP4_IFADDR, s->s);
587214423Sjamie			if (cs) {
588214423Sjamie				strcpy(s->s, cs + 1);
589214423Sjamie				s->len -= cs + 1 - s->s;
590214117Sjamie			}
591214117Sjamie			if ((cs = strchr(s->s, '/'))) {
592214117Sjamie				prefix = strtol(cs + 1, &ep, 10);
593223351Sjamie				if (*ep == '.'
594223351Sjamie				    ? inet_pton(AF_INET, cs + 1, &addr4) != 1
595223351Sjamie				    : *ep || prefix < 0 || prefix > 32) {
596223351Sjamie					jail_warnx(j,
597223351Sjamie					    "ip4.addr: bad netmask \"%s\"", cs);
598223351Sjamie					error = -1;
599223351Sjamie				}
600239601Sjamie				s->len = cs - s->s;
601223351Sjamie			}
602223351Sjamie		}
603223351Sjamie	}
604214433Sjamie#endif
605214433Sjamie#ifdef INET6
606223351Sjamie	if (j->intparams[KP_IP6_ADDR] != NULL) {
607223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
608223351Sjamie			cs = strchr(s->s, '|');
609223351Sjamie			if (cs || defif)
610223351Sjamie				add_param(j, NULL, IP__IP6_IFADDR, s->s);
611223351Sjamie			if (cs) {
612223351Sjamie				strcpy(s->s, cs + 1);
613223351Sjamie				s->len -= cs + 1 - s->s;
614223351Sjamie			}
615223351Sjamie			if ((cs = strchr(s->s, '/'))) {
616223351Sjamie				prefix = strtol(cs + 1, &ep, 10);
617223351Sjamie				if (*ep || prefix < 0 || prefix > 128) {
618214433Sjamie					jail_warnx(j,
619223351Sjamie					    "ip6.addr: bad prefixlen \"%s\"",
620214117Sjamie					    cs);
621214117Sjamie					error = -1;
622214117Sjamie				}
623239601Sjamie				s->len = cs - s->s;
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) {
715239601Sjamie				memcpy(cs, s->s, s->len);
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