1214117Sjamie/*-
2223190Sjamie * Copyright (c) 2011 James Gritton
3214117Sjamie * All rights reserved.
4214117Sjamie *
5214117Sjamie * Redistribution and use in source and binary forms, with or without
6214117Sjamie * modification, are permitted provided that the following conditions
7214117Sjamie * are met:
8214117Sjamie * 1. Redistributions of source code must retain the above copyright
9214117Sjamie *    notice, this list of conditions and the following disclaimer.
10214117Sjamie * 2. Redistributions in binary form must reproduce the above copyright
11214117Sjamie *    notice, this list of conditions and the following disclaimer in the
12214117Sjamie *    documentation and/or other materials provided with the distribution.
13214117Sjamie *
14214117Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15214117Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16214117Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17214117Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18214117Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19214117Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20214117Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21214117Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22214117Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23214117Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24214117Sjamie * SUCH DAMAGE.
25214117Sjamie */
26214117Sjamie
27214117Sjamie#include <sys/cdefs.h>
28214117Sjamie__FBSDID("$FreeBSD$");
29214117Sjamie
30214117Sjamie#include <sys/types.h>
31214783Sjamie#include <sys/errno.h>
32214117Sjamie#include <sys/socket.h>
33214117Sjamie#include <sys/sysctl.h>
34214117Sjamie
35214117Sjamie#include <arpa/inet.h>
36214117Sjamie#include <netinet/in.h>
37214117Sjamie
38214117Sjamie#include <err.h>
39214117Sjamie#include <netdb.h>
40214117Sjamie#include <stdio.h>
41214117Sjamie#include <stdlib.h>
42214117Sjamie#include <string.h>
43223351Sjamie#include <unistd.h>
44214117Sjamie
45214117Sjamie#include "jailp.h"
46214117Sjamie
47214117Sjamiestruct ipspec {
48214117Sjamie	const char	*name;
49214117Sjamie	unsigned	flags;
50214117Sjamie};
51214117Sjamie
52214117Sjamieextern FILE *yyin;
53214117Sjamieextern int yynerrs;
54214117Sjamie
55235789Sbaptextern int yyparse(void);
56235789Sbapt
57214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
58214117Sjamie
59214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p);
60214117Sjamiestatic void free_param_strings(struct cfparam *p);
61214117Sjamie
62214117Sjamiestatic const struct ipspec intparams[] = {
63214423Sjamie    [IP_ALLOW_DYING] =		{"allow.dying",		PF_INTERNAL | PF_BOOL},
64214423Sjamie    [IP_COMMAND] =		{"command",		PF_INTERNAL},
65214423Sjamie    [IP_DEPEND] =		{"depend",		PF_INTERNAL},
66214423Sjamie    [IP_EXEC_CLEAN] =		{"exec.clean",		PF_INTERNAL | PF_BOOL},
67214423Sjamie    [IP_EXEC_CONSOLELOG] =	{"exec.consolelog",	PF_INTERNAL},
68214423Sjamie    [IP_EXEC_FIB] =		{"exec.fib",		PF_INTERNAL | PF_INT},
69214423Sjamie    [IP_EXEC_JAIL_USER] =	{"exec.jail_user",	PF_INTERNAL},
70214423Sjamie    [IP_EXEC_POSTSTART] =	{"exec.poststart",	PF_INTERNAL},
71214423Sjamie    [IP_EXEC_POSTSTOP] =	{"exec.poststop",	PF_INTERNAL},
72214423Sjamie    [IP_EXEC_PRESTART] =	{"exec.prestart",	PF_INTERNAL},
73214423Sjamie    [IP_EXEC_PRESTOP] =		{"exec.prestop",	PF_INTERNAL},
74214423Sjamie    [IP_EXEC_START] =		{"exec.start",		PF_INTERNAL},
75214423Sjamie    [IP_EXEC_STOP] =		{"exec.stop",		PF_INTERNAL},
76214423Sjamie    [IP_EXEC_SYSTEM_JAIL_USER]=	{"exec.system_jail_user",
77214423Sjamie							PF_INTERNAL | PF_BOOL},
78214423Sjamie    [IP_EXEC_SYSTEM_USER] =	{"exec.system_user",	PF_INTERNAL},
79214423Sjamie    [IP_EXEC_TIMEOUT] =		{"exec.timeout",	PF_INTERNAL | PF_INT},
80223351Sjamie#if defined(INET) || defined(INET6)
81214423Sjamie    [IP_INTERFACE] =		{"interface",		PF_INTERNAL},
82214423Sjamie    [IP_IP_HOSTNAME] =		{"ip_hostname",		PF_INTERNAL | PF_BOOL},
83223351Sjamie#endif
84248854Sjamie    [IP_MOUNT] =		{"mount",		PF_INTERNAL | PF_REV},
85214423Sjamie    [IP_MOUNT_DEVFS] =		{"mount.devfs",		PF_INTERNAL | PF_BOOL},
86256387Shrs    [IP_MOUNT_FDESCFS] =	{"mount.fdescfs",	PF_INTERNAL | PF_BOOL},
87214423Sjamie    [IP_MOUNT_FSTAB] =		{"mount.fstab",		PF_INTERNAL},
88214423Sjamie    [IP_STOP_TIMEOUT] =		{"stop.timeout",	PF_INTERNAL | PF_INT},
89214423Sjamie    [IP_VNET_INTERFACE] =	{"vnet.interface",	PF_INTERNAL},
90223351Sjamie#ifdef INET
91248854Sjamie    [IP__IP4_IFADDR] =		{"ip4.addr",	PF_INTERNAL | PF_CONV | PF_REV},
92223351Sjamie#endif
93214117Sjamie#ifdef INET6
94248854Sjamie    [IP__IP6_IFADDR] =		{"ip6.addr",	PF_INTERNAL | PF_CONV | PF_REV},
95214117Sjamie#endif
96248854Sjamie    [IP__MOUNT_FROM_FSTAB] =	{"mount.fstab",	PF_INTERNAL | PF_CONV | PF_REV},
97223189Sjamie    [IP__OP] =			{NULL,			PF_CONV},
98214423Sjamie    [KP_ALLOW_CHFLAGS] =	{"allow.chflags",	0},
99214423Sjamie    [KP_ALLOW_MOUNT] =		{"allow.mount",		0},
100214423Sjamie    [KP_ALLOW_RAW_SOCKETS] =	{"allow.raw_sockets",	0},
101214423Sjamie    [KP_ALLOW_SET_HOSTNAME]=	{"allow.set_hostname",	0},
102214423Sjamie    [KP_ALLOW_SOCKET_AF] =	{"allow.socket_af",	0},
103214423Sjamie    [KP_ALLOW_SYSVIPC] =	{"allow.sysvipc",	0},
104232242Sjamie    [KP_DEVFS_RULESET] =	{"devfs_ruleset",	0},
105214423Sjamie    [KP_ENFORCE_STATFS] =	{"enforce_statfs",	0},
106214423Sjamie    [KP_HOST_HOSTNAME] =	{"host.hostname",	0},
107223351Sjamie#ifdef INET
108214423Sjamie    [KP_IP4_ADDR] =		{"ip4.addr",		0},
109223351Sjamie#endif
110214423Sjamie#ifdef INET6
111214423Sjamie    [KP_IP6_ADDR] =		{"ip6.addr",		0},
112214423Sjamie#endif
113214423Sjamie    [KP_JID] =			{"jid",			0},
114214423Sjamie    [KP_NAME] =			{"name",		0},
115214423Sjamie    [KP_PATH] =			{"path",		0},
116214423Sjamie    [KP_PERSIST] =		{"persist",		0},
117214423Sjamie    [KP_SECURELEVEL] =		{"securelevel",		0},
118214423Sjamie    [KP_VNET] =			{"vnet",		0},
119214117Sjamie};
120214117Sjamie
121214117Sjamie/*
122214117Sjamie * Parse the jail configuration file.
123214117Sjamie */
124214117Sjamievoid
125214117Sjamieload_config(void)
126214117Sjamie{
127214117Sjamie	struct cfjails wild;
128214117Sjamie	struct cfparams opp;
129214117Sjamie	struct cfjail *j, *tj, *wj;
130214117Sjamie	struct cfparam *p, *vp, *tp;
131214117Sjamie	struct cfstring *s, *vs, *ns;
132214117Sjamie	struct cfvar *v;
133214117Sjamie	char *ep;
134214117Sjamie	size_t varoff;
135214117Sjamie	int did_self, jseq, pgen;
136214117Sjamie
137214117Sjamie	if (!strcmp(cfname, "-")) {
138214117Sjamie		cfname = "STDIN";
139214117Sjamie		yyin = stdin;
140214117Sjamie	} else {
141214117Sjamie		yyin = fopen(cfname, "r");
142214117Sjamie		if (!yyin)
143214117Sjamie			err(1, "%s", cfname);
144214117Sjamie	}
145214117Sjamie	if (yyparse() || yynerrs)
146214117Sjamie		exit(1);
147214117Sjamie
148214117Sjamie	/* Separate the wildcard jails out from the actual jails. */
149214117Sjamie	jseq = 0;
150214117Sjamie	TAILQ_INIT(&wild);
151214117Sjamie	TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
152214117Sjamie		j->seq = ++jseq;
153214117Sjamie		if (wild_jail_name(j->name))
154214117Sjamie			requeue(j, &wild);
155214117Sjamie	}
156214117Sjamie
157214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq) {
158214117Sjamie		/* Set aside the jail's parameters. */
159214117Sjamie		TAILQ_INIT(&opp);
160214117Sjamie		TAILQ_CONCAT(&opp, &j->params, tq);
161214117Sjamie		/*
162214117Sjamie		 * The jail name implies its "name" or "jid" parameter,
163214117Sjamie		 * though they may also be explicitly set later on.
164214117Sjamie		 */
165214117Sjamie		add_param(j, NULL,
166214423Sjamie		    strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
167214117Sjamie		    j->name);
168214117Sjamie		/*
169214117Sjamie		 * Collect parameters for the jail, global parameters/variables,
170214117Sjamie		 * and any matching wildcard jails.
171214117Sjamie		 */
172214117Sjamie		did_self = 0;
173214117Sjamie		TAILQ_FOREACH(wj, &wild, tq) {
174214117Sjamie			if (j->seq < wj->seq && !did_self) {
175214117Sjamie				TAILQ_FOREACH(p, &opp, tq)
176214423Sjamie					add_param(j, p, 0, NULL);
177214117Sjamie				did_self = 1;
178214117Sjamie			}
179214117Sjamie			if (wild_jail_match(j->name, wj->name))
180214117Sjamie				TAILQ_FOREACH(p, &wj->params, tq)
181214423Sjamie					add_param(j, p, 0, NULL);
182214117Sjamie		}
183214117Sjamie		if (!did_self)
184214117Sjamie			TAILQ_FOREACH(p, &opp, tq)
185214423Sjamie				add_param(j, p, 0, NULL);
186214117Sjamie
187214117Sjamie		/* Resolve any variable substitutions. */
188214117Sjamie		pgen = 0;
189214117Sjamie		TAILQ_FOREACH(p, &j->params, tq) {
190214117Sjamie		    p->gen = ++pgen;
191214117Sjamie		find_vars:
192223188Sjamie		    TAILQ_FOREACH(s, &p->val, tq) {
193214117Sjamie			varoff = 0;
194214117Sjamie			while ((v = STAILQ_FIRST(&s->vars))) {
195214117Sjamie				TAILQ_FOREACH(vp, &j->params, tq)
196214117Sjamie					if (!strcmp(vp->name, v->name))
197214117Sjamie						break;
198214117Sjamie				if (!vp) {
199214117Sjamie					jail_warnx(j,
200214117Sjamie					    "%s: variable \"%s\" not found",
201214117Sjamie					    p->name, v->name);
202214117Sjamie				bad_var:
203214117Sjamie					j->flags |= JF_FAILED;
204214117Sjamie					TAILQ_FOREACH(vp, &j->params, tq)
205214117Sjamie						if (vp->gen == pgen)
206214117Sjamie							vp->flags |= PF_BAD;
207214117Sjamie					goto free_var;
208214117Sjamie				}
209214117Sjamie				if (vp->flags & PF_BAD)
210214117Sjamie					goto bad_var;
211214117Sjamie				if (vp->gen == pgen) {
212214117Sjamie					jail_warnx(j, "%s: variable loop",
213214117Sjamie					    v->name);
214214117Sjamie					goto bad_var;
215214117Sjamie				}
216223188Sjamie				TAILQ_FOREACH(vs, &vp->val, tq)
217214117Sjamie					if (!STAILQ_EMPTY(&vs->vars)) {
218214117Sjamie						vp->gen = pgen;
219214117Sjamie						TAILQ_REMOVE(&j->params, vp,
220214117Sjamie						    tq);
221214117Sjamie						TAILQ_INSERT_BEFORE(p, vp, tq);
222214117Sjamie						p = vp;
223214117Sjamie						goto find_vars;
224214117Sjamie					}
225223188Sjamie				vs = TAILQ_FIRST(&vp->val);
226223188Sjamie				if (TAILQ_NEXT(vs, tq) != NULL &&
227214117Sjamie				    (s->s[0] != '\0' ||
228214117Sjamie				     STAILQ_NEXT(v, tq))) {
229214117Sjamie					jail_warnx(j, "%s: array cannot be "
230214117Sjamie					    "substituted inline",
231214117Sjamie					    p->name);
232214117Sjamie					goto bad_var;
233214117Sjamie				}
234214117Sjamie				s->s = erealloc(s->s, s->len + vs->len + 1);
235214117Sjamie				memmove(s->s + v->pos + varoff + vs->len,
236214117Sjamie				    s->s + v->pos + varoff,
237214117Sjamie				    s->len - (v->pos + varoff) + 1);
238214117Sjamie				memcpy(s->s + v->pos + varoff, vs->s, vs->len);
239214117Sjamie				varoff += vs->len;
240214117Sjamie				s->len += vs->len;
241223188Sjamie				while ((vs = TAILQ_NEXT(vs, tq))) {
242214117Sjamie					ns = emalloc(sizeof(struct cfstring));
243214117Sjamie					ns->s = estrdup(vs->s);
244214117Sjamie					ns->len = vs->len;
245214117Sjamie					STAILQ_INIT(&ns->vars);
246223188Sjamie					TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
247214117Sjamie					s = ns;
248214117Sjamie				}
249214117Sjamie			free_var:
250214117Sjamie				free(v->name);
251214117Sjamie				STAILQ_REMOVE_HEAD(&s->vars, tq);
252214117Sjamie				free(v);
253214117Sjamie			}
254214117Sjamie		    }
255214117Sjamie		}
256214117Sjamie
257214117Sjamie		/* Free the jail's original parameter list and any variables. */
258214117Sjamie		while ((p = TAILQ_FIRST(&opp)))
259214117Sjamie			free_param(&opp, p);
260214117Sjamie		TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
261214117Sjamie			if (p->flags & PF_VAR)
262214117Sjamie				free_param(&j->params, p);
263214117Sjamie	}
264214117Sjamie	while ((wj = TAILQ_FIRST(&wild))) {
265214117Sjamie		free(wj->name);
266214117Sjamie		while ((p = TAILQ_FIRST(&wj->params)))
267214117Sjamie			free_param(&wj->params, p);
268214117Sjamie		TAILQ_REMOVE(&wild, wj, tq);
269214117Sjamie	}
270214117Sjamie}
271214117Sjamie
272214117Sjamie/*
273214117Sjamie * Create a new jail record.
274214117Sjamie */
275214117Sjamiestruct cfjail *
276214117Sjamieadd_jail(void)
277214117Sjamie{
278214117Sjamie	struct cfjail *j;
279214117Sjamie
280214117Sjamie	j = emalloc(sizeof(struct cfjail));
281214117Sjamie	memset(j, 0, sizeof(struct cfjail));
282214117Sjamie	TAILQ_INIT(&j->params);
283214117Sjamie	STAILQ_INIT(&j->dep[DEP_FROM]);
284214117Sjamie	STAILQ_INIT(&j->dep[DEP_TO]);
285214117Sjamie	j->queue = &cfjails;
286214117Sjamie	TAILQ_INSERT_TAIL(&cfjails, j, tq);
287214117Sjamie	return j;
288214117Sjamie}
289214117Sjamie
290214117Sjamie/*
291214117Sjamie * Add a parameter to a jail.
292214117Sjamie */
293214117Sjamievoid
294214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
295214117Sjamie    const char *value)
296214117Sjamie{
297214117Sjamie	struct cfstrings nss;
298214117Sjamie	struct cfparam *dp, *np;
299214117Sjamie	struct cfstring *s, *ns;
300214117Sjamie	struct cfvar *v, *nv;
301214423Sjamie	const char *name;
302214423Sjamie	char *cs, *tname;
303214117Sjamie	unsigned flags;
304214117Sjamie
305214117Sjamie	if (j == NULL) {
306214117Sjamie		/* Create a single anonymous jail if one doesn't yet exist. */
307214117Sjamie		j = TAILQ_LAST(&cfjails, cfjails);
308214117Sjamie		if (j == NULL)
309214117Sjamie			j = add_jail();
310214117Sjamie	}
311223188Sjamie	TAILQ_INIT(&nss);
312214117Sjamie	if (p != NULL) {
313214117Sjamie		name = p->name;
314214117Sjamie		flags = p->flags;
315214117Sjamie		/*
316214117Sjamie		 * Make a copy of the parameter's string list,
317214117Sjamie		 * which may be freed if it's overridden later.
318214117Sjamie		 */
319223188Sjamie		TAILQ_FOREACH(s, &p->val, tq) {
320214117Sjamie			ns = emalloc(sizeof(struct cfstring));
321214117Sjamie			ns->s = estrdup(s->s);
322214117Sjamie			ns->len = s->len;
323214117Sjamie			STAILQ_INIT(&ns->vars);
324214117Sjamie			STAILQ_FOREACH(v, &s->vars, tq) {
325214117Sjamie				nv = emalloc(sizeof(struct cfvar));
326214117Sjamie				nv->name = strdup(v->name);
327214117Sjamie				nv->pos = v->pos;
328214117Sjamie				STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
329214117Sjamie			}
330223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
331214117Sjamie		}
332214117Sjamie	} else {
333214117Sjamie		flags = PF_APPEND;
334234988Sjamie		if (ipnum != IP__NULL) {
335214423Sjamie			name = intparams[ipnum].name;
336214423Sjamie			flags |= intparams[ipnum].flags;
337214423Sjamie		} else if ((cs = strchr(value, '='))) {
338214423Sjamie			tname = alloca(cs - value + 1);
339214423Sjamie			strlcpy(tname, value, cs - value + 1);
340214423Sjamie			name = tname;
341214423Sjamie			value = cs + 1;
342214423Sjamie		} else {
343214423Sjamie			name = value;
344214423Sjamie			value = NULL;
345214423Sjamie		}
346214117Sjamie		if (value != NULL) {
347214117Sjamie			ns = emalloc(sizeof(struct cfstring));
348214117Sjamie			ns->s = estrdup(value);
349214117Sjamie			ns->len = strlen(value);
350214117Sjamie			STAILQ_INIT(&ns->vars);
351223188Sjamie			TAILQ_INSERT_TAIL(&nss, ns, tq);
352214117Sjamie		}
353214117Sjamie	}
354214117Sjamie
355214117Sjamie	/* See if this parameter has already been added. */
356234988Sjamie	if (ipnum != IP__NULL)
357214423Sjamie		dp = j->intparams[ipnum];
358214423Sjamie	else
359214423Sjamie		TAILQ_FOREACH(dp, &j->params, tq)
360214423Sjamie			if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
361214423Sjamie				break;
362214423Sjamie	if (dp != NULL) {
363214423Sjamie		/* Found it - append or replace. */
364214423Sjamie		if (strcmp(dp->name, name)) {
365214423Sjamie			free(dp->name);
366214423Sjamie			dp->name = estrdup(name);
367214117Sjamie		}
368223188Sjamie		if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
369214423Sjamie			free_param_strings(dp);
370223188Sjamie		TAILQ_CONCAT(&dp->val, &nss, tq);
371214423Sjamie		dp->flags |= flags;
372214423Sjamie	} else {
373214117Sjamie		/* Not found - add it. */
374214117Sjamie		np = emalloc(sizeof(struct cfparam));
375214117Sjamie		np->name = estrdup(name);
376223188Sjamie		TAILQ_INIT(&np->val);
377223188Sjamie		TAILQ_CONCAT(&np->val, &nss, tq);
378214117Sjamie		np->flags = flags;
379214117Sjamie		np->gen = 0;
380214117Sjamie		TAILQ_INSERT_TAIL(&j->params, np, tq);
381234988Sjamie		if (ipnum != IP__NULL)
382214423Sjamie			j->intparams[ipnum] = np;
383214423Sjamie		else
384234988Sjamie			for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
385214423Sjamie				if (!(intparams[ipnum].flags & PF_CONV) &&
386214423Sjamie				    equalopts(name, intparams[ipnum].name)) {
387214423Sjamie					j->intparams[ipnum] = np;
388214423Sjamie					np->flags |= intparams[ipnum].flags;
389214423Sjamie					break;
390214423Sjamie				}
391214117Sjamie	}
392214117Sjamie}
393214117Sjamie
394214117Sjamie/*
395214117Sjamie * Return if a boolean parameter exists and is true.
396214117Sjamie */
397214117Sjamieint
398214117Sjamiebool_param(const struct cfparam *p)
399214117Sjamie{
400214117Sjamie	const char *cs;
401214117Sjamie
402214117Sjamie	if (p == NULL)
403214117Sjamie		return 0;
404214117Sjamie	cs = strrchr(p->name, '.');
405214117Sjamie	return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
406223188Sjamie	    (TAILQ_EMPTY(&p->val) ||
407223188Sjamie	     !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
408223188Sjamie	     (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
409214117Sjamie}
410214117Sjamie
411214117Sjamie/*
412214117Sjamie * Set an integer if a parameter if it exists.
413214117Sjamie */
414214117Sjamieint
415214117Sjamieint_param(const struct cfparam *p, int *ip)
416214117Sjamie{
417223188Sjamie	if (p == NULL || TAILQ_EMPTY(&p->val))
418214117Sjamie		return 0;
419223188Sjamie	*ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
420214117Sjamie	return 1;
421214117Sjamie}
422214117Sjamie
423214117Sjamie/*
424214117Sjamie * Return the string value of a scalar parameter if it exists.
425214117Sjamie */
426214117Sjamieconst char *
427214117Sjamiestring_param(const struct cfparam *p)
428214117Sjamie{
429223188Sjamie	return (p && !TAILQ_EMPTY(&p->val)
430223188Sjamie	    ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
431214117Sjamie}
432214117Sjamie
433214117Sjamie/*
434214649Sjamie * Check syntax and values of internal parameters.  Set some internal
435214649Sjamie * parameters based on the values of others.
436214117Sjamie */
437214117Sjamieint
438214649Sjamiecheck_intparams(struct cfjail *j)
439214117Sjamie{
440214649Sjamie	struct cfparam *p;
441223327Sjamie	struct cfstring *s;
442214783Sjamie	FILE *f;
443223351Sjamie	const char *val;
444214783Sjamie	char *cs, *ep, *ln;
445223351Sjamie	size_t lnlen;
446223351Sjamie	int error;
447223351Sjamie#if defined(INET) || defined(INET6)
448223351Sjamie	struct addrinfo hints;
449223351Sjamie	struct addrinfo *ai0, *ai;
450223351Sjamie	const char *hostname;
451223351Sjamie	int gicode, defif, prefix;
452223351Sjamie#endif
453223351Sjamie#ifdef INET
454223351Sjamie	struct in_addr addr4;
455223351Sjamie	int ip4ok;
456214117Sjamie	char avalue4[INET_ADDRSTRLEN];
457223351Sjamie#endif
458214117Sjamie#ifdef INET6
459214117Sjamie	struct in6_addr addr6;
460223351Sjamie	int ip6ok;
461214117Sjamie	char avalue6[INET6_ADDRSTRLEN];
462214117Sjamie#endif
463214117Sjamie
464214117Sjamie	error = 0;
465214649Sjamie	/* Check format of boolan and integer values. */
466214649Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
467223188Sjamie		if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
468223188Sjamie			val = TAILQ_LAST(&p->val, cfstrings)->s;
469214649Sjamie			if (p->flags & PF_BOOL) {
470214649Sjamie				if (strcasecmp(val, "false") &&
471214649Sjamie				    strcasecmp(val, "true") &&
472214649Sjamie				    ((void)strtol(val, &ep, 10), *ep)) {
473214649Sjamie					jail_warnx(j,
474214649Sjamie					    "%s: unknown boolean value \"%s\"",
475214649Sjamie					    p->name, val);
476214649Sjamie					error = -1;
477214649Sjamie				}
478214649Sjamie			} else {
479214649Sjamie				(void)strtol(val, &ep, 10);
480214649Sjamie				if (ep == val || *ep) {
481214649Sjamie					jail_warnx(j,
482214649Sjamie					    "%s: non-integer value \"%s\"",
483214649Sjamie					    p->name, val);
484214649Sjamie					error = -1;
485214649Sjamie				}
486214649Sjamie			}
487214649Sjamie		}
488214649Sjamie	}
489214649Sjamie
490223351Sjamie#if defined(INET) || defined(INET6)
491214117Sjamie	/*
492214117Sjamie	 * The ip_hostname parameter looks up the hostname, and adds parameters
493214117Sjamie	 * for any IP addresses it finds.
494214117Sjamie	 */
495214649Sjamie	if (((j->flags & JF_OP_MASK) != JF_STOP ||
496214649Sjamie	    j->intparams[IP_INTERFACE] != NULL) &&
497214649Sjamie	    bool_param(j->intparams[IP_IP_HOSTNAME]) &&
498214423Sjamie	    (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
499214117Sjamie		j->intparams[IP_IP_HOSTNAME] = NULL;
500214117Sjamie		/*
501214117Sjamie		 * Silently ignore unsupported address families from
502214117Sjamie		 * DNS lookups.
503214117Sjamie		 */
504223351Sjamie#ifdef INET
505223351Sjamie		ip4ok = feature_present("inet");
506214117Sjamie#endif
507214117Sjamie#ifdef INET6
508223351Sjamie		ip6ok = feature_present("inet6");
509214117Sjamie#endif
510223351Sjamie		if (
511223351Sjamie#if defined(INET) && defined(INET6)
512223351Sjamie		    ip4ok || ip6ok
513223351Sjamie#elif defined(INET)
514223351Sjamie		    ip4ok
515223351Sjamie#elif defined(INET6)
516223351Sjamie		    ip6ok
517223351Sjamie#endif
518223351Sjamie			 ) {
519214117Sjamie			/* Look up the hostname (or get the address) */
520214117Sjamie			memset(&hints, 0, sizeof(hints));
521214117Sjamie			hints.ai_socktype = SOCK_STREAM;
522214117Sjamie			hints.ai_family =
523223351Sjamie#if defined(INET) && defined(INET6)
524223351Sjamie			    ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
525223351Sjamie#elif defined(INET)
526223351Sjamie			    PF_INET;
527223351Sjamie#elif defined(INET6)
528223351Sjamie			    PF_INET6;
529214117Sjamie#endif
530214649Sjamie			gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
531214649Sjamie			if (gicode != 0) {
532214117Sjamie				jail_warnx(j, "host.hostname %s: %s", hostname,
533214649Sjamie				    gai_strerror(gicode));
534214117Sjamie				error = -1;
535214117Sjamie			} else {
536214117Sjamie				/*
537214117Sjamie				 * Convert the addresses to ASCII so jailparam
538214117Sjamie				 * can convert them back.  Errors are not
539214117Sjamie				 * expected here.
540214117Sjamie				 */
541214117Sjamie				for (ai = ai0; ai; ai = ai->ai_next)
542214117Sjamie					switch (ai->ai_family) {
543223351Sjamie#ifdef INET
544214117Sjamie					case AF_INET:
545214117Sjamie						memcpy(&addr4,
546214117Sjamie						    &((struct sockaddr_in *)
547214117Sjamie						    (void *)ai->ai_addr)->
548214117Sjamie						    sin_addr, sizeof(addr4));
549214117Sjamie						if (inet_ntop(AF_INET,
550214117Sjamie						    &addr4, avalue4,
551214117Sjamie						    INET_ADDRSTRLEN) == NULL)
552214117Sjamie							err(1, "inet_ntop");
553214423Sjamie						add_param(j, NULL, KP_IP4_ADDR,
554214117Sjamie						    avalue4);
555214117Sjamie						break;
556223351Sjamie#endif
557214117Sjamie#ifdef INET6
558214117Sjamie					case AF_INET6:
559214117Sjamie						memcpy(&addr6,
560214117Sjamie						    &((struct sockaddr_in6 *)
561214117Sjamie						    (void *)ai->ai_addr)->
562214117Sjamie						    sin6_addr, sizeof(addr6));
563214117Sjamie						if (inet_ntop(AF_INET6,
564214117Sjamie						    &addr6, avalue6,
565214117Sjamie						    INET6_ADDRSTRLEN) == NULL)
566214117Sjamie							err(1, "inet_ntop");
567214423Sjamie						add_param(j, NULL, KP_IP6_ADDR,
568214117Sjamie						    avalue6);
569214117Sjamie						break;
570214117Sjamie#endif
571214117Sjamie					}
572214117Sjamie				freeaddrinfo(ai0);
573214117Sjamie			}
574214117Sjamie		}
575214117Sjamie	}
576214649Sjamie
577214117Sjamie	/*
578214117Sjamie	 * IP addresses may include an interface to set that address on,
579214117Sjamie	 * and a netmask/suffix for that address.
580214117Sjamie	 */
581214117Sjamie	defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
582223351Sjamie#ifdef INET
583223351Sjamie	if (j->intparams[KP_IP4_ADDR] != NULL) {
584223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
585214117Sjamie			cs = strchr(s->s, '|');
586214423Sjamie			if (cs || defif)
587223351Sjamie				add_param(j, NULL, IP__IP4_IFADDR, s->s);
588214423Sjamie			if (cs) {
589214423Sjamie				strcpy(s->s, cs + 1);
590214423Sjamie				s->len -= cs + 1 - s->s;
591214117Sjamie			}
592214117Sjamie			if ((cs = strchr(s->s, '/'))) {
593214117Sjamie				prefix = strtol(cs + 1, &ep, 10);
594223351Sjamie				if (*ep == '.'
595223351Sjamie				    ? inet_pton(AF_INET, cs + 1, &addr4) != 1
596223351Sjamie				    : *ep || prefix < 0 || prefix > 32) {
597223351Sjamie					jail_warnx(j,
598223351Sjamie					    "ip4.addr: bad netmask \"%s\"", cs);
599223351Sjamie					error = -1;
600223351Sjamie				}
601239621Sjamie				*cs = '\0';
602239601Sjamie				s->len = cs - s->s;
603223351Sjamie			}
604223351Sjamie		}
605223351Sjamie	}
606214433Sjamie#endif
607214433Sjamie#ifdef INET6
608223351Sjamie	if (j->intparams[KP_IP6_ADDR] != NULL) {
609223351Sjamie		TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
610223351Sjamie			cs = strchr(s->s, '|');
611223351Sjamie			if (cs || defif)
612223351Sjamie				add_param(j, NULL, IP__IP6_IFADDR, s->s);
613223351Sjamie			if (cs) {
614223351Sjamie				strcpy(s->s, cs + 1);
615223351Sjamie				s->len -= cs + 1 - s->s;
616223351Sjamie			}
617223351Sjamie			if ((cs = strchr(s->s, '/'))) {
618223351Sjamie				prefix = strtol(cs + 1, &ep, 10);
619223351Sjamie				if (*ep || prefix < 0 || prefix > 128) {
620214433Sjamie					jail_warnx(j,
621223351Sjamie					    "ip6.addr: bad prefixlen \"%s\"",
622214117Sjamie					    cs);
623214117Sjamie					error = -1;
624214117Sjamie				}
625239621Sjamie				*cs = '\0';
626239601Sjamie				s->len = cs - s->s;
627214117Sjamie			}
628214117Sjamie		}
629214117Sjamie	}
630214117Sjamie#endif
631223351Sjamie#endif
632214783Sjamie
633214783Sjamie	/*
634214783Sjamie	 * Read mount.fstab file(s), and treat each line as its own mount
635214783Sjamie	 * parameter.
636214783Sjamie	 */
637214783Sjamie	if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
638223188Sjamie		TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
639214783Sjamie			if (s->len == 0)
640214783Sjamie				continue;
641214783Sjamie			f = fopen(s->s, "r");
642214783Sjamie			if (f == NULL) {
643214783Sjamie				jail_warnx(j, "mount.fstab: %s: %s",
644214783Sjamie				    s->s, strerror(errno));
645214783Sjamie				error = -1;
646214783Sjamie				continue;
647214783Sjamie			}
648214783Sjamie			while ((ln = fgetln(f, &lnlen))) {
649214783Sjamie				if ((cs = memchr(ln, '#', lnlen - 1)))
650214783Sjamie					lnlen = cs - ln + 1;
651214783Sjamie				if (ln[lnlen - 1] == '\n' ||
652214783Sjamie				    ln[lnlen - 1] == '#')
653214783Sjamie					ln[lnlen - 1] = '\0';
654214783Sjamie				else {
655214783Sjamie					cs = alloca(lnlen + 1);
656214783Sjamie					strlcpy(cs, ln, lnlen + 1);
657214783Sjamie					ln = cs;
658214783Sjamie				}
659214783Sjamie				add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
660214783Sjamie			}
661214783Sjamie			fclose(f);
662214783Sjamie		}
663214783Sjamie	}
664214783Sjamie	if (error)
665214783Sjamie		failed(j);
666214117Sjamie	return error;
667214117Sjamie}
668214117Sjamie
669214117Sjamie/*
670214117Sjamie * Import parameters into libjail's binary jailparam format.
671214117Sjamie */
672214117Sjamieint
673214117Sjamieimport_params(struct cfjail *j)
674214117Sjamie{
675214117Sjamie	struct cfparam *p;
676214117Sjamie	struct cfstring *s, *ts;
677214117Sjamie	struct jailparam *jp;
678214117Sjamie	char *value, *cs;
679214117Sjamie	size_t vallen;
680214117Sjamie	int error;
681214117Sjamie
682214117Sjamie	error = 0;
683214117Sjamie	j->njp = 0;
684214117Sjamie	TAILQ_FOREACH(p, &j->params, tq)
685214117Sjamie		if (!(p->flags & PF_INTERNAL))
686214117Sjamie			j->njp++;
687214117Sjamie	j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
688214117Sjamie	TAILQ_FOREACH(p, &j->params, tq) {
689214117Sjamie		if (p->flags & PF_INTERNAL)
690214117Sjamie			continue;
691214117Sjamie		if (jailparam_init(jp, p->name) < 0) {
692214117Sjamie			error = -1;
693214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
694241196Sjamie			jp++;
695214117Sjamie			continue;
696214117Sjamie		}
697223188Sjamie		if (TAILQ_EMPTY(&p->val))
698214117Sjamie			value = NULL;
699214117Sjamie		else if (!jp->jp_elemlen ||
700223188Sjamie			 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
701214117Sjamie			/*
702214117Sjamie			 * Scalar parameters silently discard multiple (array)
703214117Sjamie			 * values, keeping only the last value added.  This
704214117Sjamie			 * lets values added from the command line append to
705214117Sjamie			 * arrays wthout pre-checking the type.
706214117Sjamie			 */
707223188Sjamie			value = TAILQ_LAST(&p->val, cfstrings)->s;
708214117Sjamie		} else {
709214117Sjamie			/*
710214117Sjamie			 * Convert arrays into comma-separated strings, which
711214117Sjamie			 * jailparam_import will then convert back into arrays.
712214117Sjamie			 */
713214117Sjamie			vallen = 0;
714223188Sjamie			TAILQ_FOREACH(s, &p->val, tq)
715214117Sjamie				vallen += s->len + 1;
716214117Sjamie			value = alloca(vallen);
717214117Sjamie			cs = value;
718223188Sjamie			TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
719239601Sjamie				memcpy(cs, s->s, s->len);
720239621Sjamie				cs += s->len + 1;
721239621Sjamie				cs[-1] = ',';
722214117Sjamie			}
723239621Sjamie			value[vallen - 1] = '\0';
724214117Sjamie		}
725214117Sjamie		if (jailparam_import(jp, value) < 0) {
726214117Sjamie			error = -1;
727214117Sjamie			jail_warnx(j, "%s", jail_errmsg);
728214117Sjamie		}
729214117Sjamie		jp++;
730214117Sjamie	}
731214117Sjamie	if (error) {
732214117Sjamie		jailparam_free(j->jp, j->njp);
733214117Sjamie		free(j->jp);
734214117Sjamie		j->jp = NULL;
735214117Sjamie		failed(j);
736214117Sjamie	}
737214117Sjamie	return error;
738214117Sjamie}
739214117Sjamie
740214117Sjamie/*
741214117Sjamie * Check if options are equal (with or without the "no" prefix).
742214117Sjamie */
743214117Sjamieint
744214117Sjamieequalopts(const char *opt1, const char *opt2)
745214117Sjamie{
746214117Sjamie	char *p;
747214117Sjamie
748214117Sjamie	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
749214117Sjamie	if (strcmp(opt1, opt2) == 0)
750214117Sjamie		return (1);
751214117Sjamie	/* "noopt" vs. "opt" */
752214117Sjamie	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
753214117Sjamie		return (1);
754214117Sjamie	/* "opt" vs. "noopt" */
755214117Sjamie	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
756214117Sjamie		return (1);
757214117Sjamie	while ((p = strchr(opt1, '.')) != NULL &&
758214117Sjamie	    !strncmp(opt1, opt2, ++p - opt1)) {
759214117Sjamie		opt2 += p - opt1;
760214117Sjamie		opt1 = p;
761214117Sjamie		/* "foo.noopt" vs. "foo.opt" */
762214117Sjamie		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
763214117Sjamie			return (1);
764214117Sjamie		/* "foo.opt" vs. "foo.noopt" */
765214117Sjamie		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
766214117Sjamie			return (1);
767214117Sjamie	}
768214117Sjamie	return (0);
769214117Sjamie}
770214117Sjamie
771214117Sjamie/*
772214117Sjamie * See if a jail name matches a wildcard.
773214117Sjamie */
774214117Sjamieint
775214117Sjamiewild_jail_match(const char *jname, const char *wname)
776214117Sjamie{
777214117Sjamie	const char *jc, *jd, *wc, *wd;
778214117Sjamie
779214117Sjamie	/*
780214117Sjamie	 * A non-final "*" component in the wild name matches a single jail
781214117Sjamie	 * component, and a final "*" matches one or more jail components.
782214117Sjamie	 */
783214117Sjamie	for (jc = jname, wc = wname;
784214117Sjamie	     (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
785214117Sjamie	     jc = jd + 1, wc = wd + 1)
786214117Sjamie		if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
787214117Sjamie			return 0;
788214117Sjamie	return (!strcmp(jc, wc) || !strcmp(wc, "*"));
789214117Sjamie}
790214117Sjamie
791214117Sjamie/*
792214117Sjamie * Return if a jail name is a wildcard.
793214117Sjamie */
794214117Sjamieint
795214117Sjamiewild_jail_name(const char *wname)
796214117Sjamie{
797214117Sjamie	const char *wc;
798214117Sjamie
799214117Sjamie	for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
800214117Sjamie		if ((wc == wname || wc[-1] == '.') &&
801214117Sjamie		    (wc[1] == '\0' || wc[1] == '.'))
802214117Sjamie			return 1;
803214117Sjamie	return 0;
804214117Sjamie}
805214117Sjamie
806214117Sjamie/*
807214117Sjamie * Free a parameter record and all its strings and variables.
808214117Sjamie */
809214117Sjamiestatic void
810214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p)
811214117Sjamie{
812214117Sjamie	free(p->name);
813214117Sjamie	free_param_strings(p);
814214117Sjamie	TAILQ_REMOVE(pp, p, tq);
815214117Sjamie	free(p);
816214117Sjamie}
817214117Sjamie
818214117Sjamiestatic void
819214117Sjamiefree_param_strings(struct cfparam *p)
820214117Sjamie{
821214117Sjamie	struct cfstring *s;
822214117Sjamie	struct cfvar *v;
823214117Sjamie
824223188Sjamie	while ((s = TAILQ_FIRST(&p->val))) {
825214117Sjamie		free(s->s);
826214117Sjamie		while ((v = STAILQ_FIRST(&s->vars))) {
827214117Sjamie			free(v->name);
828214117Sjamie			STAILQ_REMOVE_HEAD(&s->vars, tq);
829214117Sjamie			free(v);
830214117Sjamie		}
831223188Sjamie		TAILQ_REMOVE(&p->val, s, tq);
832214117Sjamie		free(s);
833214117Sjamie	}
834214117Sjamie}
835