1 /*-
2 * Copyright (c) 2011 James Gritton
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/types.h>
31 #include <sys/errno.h>
32 #include <sys/socket.h>
33 #include <sys/sysctl.h>
34
35 #include <arpa/inet.h>
36 #include <netinet/in.h>
37
38 #include <err.h>
39 #include <netdb.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include "jailp.h"
46
47 struct ipspec {
48 const char *name;
49 unsigned flags;
50 };
51
52 extern FILE *yyin;
53 extern int yynerrs;
54
55 extern int yyparse(void);
56
57 struct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
58
59 static void free_param(struct cfparams *pp, struct cfparam *p);
60 static void free_param_strings(struct cfparam *p);
61
62 static const struct ipspec intparams[] = {
63 [IP_ALLOW_DYING] = {"allow.dying", PF_INTERNAL | PF_BOOL},
64 [IP_COMMAND] = {"command", PF_INTERNAL},
65 [IP_DEPEND] = {"depend", PF_INTERNAL},
66 [IP_EXEC_CLEAN] = {"exec.clean", PF_INTERNAL | PF_BOOL},
67 [IP_EXEC_CONSOLELOG] = {"exec.consolelog", PF_INTERNAL},
68 [IP_EXEC_FIB] = {"exec.fib", PF_INTERNAL | PF_INT},
69 [IP_EXEC_JAIL_USER] = {"exec.jail_user", PF_INTERNAL},
70 [IP_EXEC_POSTSTART] = {"exec.poststart", PF_INTERNAL},
71 [IP_EXEC_POSTSTOP] = {"exec.poststop", PF_INTERNAL},
72 [IP_EXEC_PRESTART] = {"exec.prestart", PF_INTERNAL},
73 [IP_EXEC_PRESTOP] = {"exec.prestop", PF_INTERNAL},
74 [IP_EXEC_START] = {"exec.start", PF_INTERNAL},
75 [IP_EXEC_STOP] = {"exec.stop", PF_INTERNAL},
76 [IP_EXEC_SYSTEM_JAIL_USER]= {"exec.system_jail_user",
77 PF_INTERNAL | PF_BOOL},
78 [IP_EXEC_SYSTEM_USER] = {"exec.system_user", PF_INTERNAL},
79 [IP_EXEC_TIMEOUT] = {"exec.timeout", PF_INTERNAL | PF_INT},
80 #if defined(INET) || defined(INET6)
81 [IP_INTERFACE] = {"interface", PF_INTERNAL},
82 [IP_IP_HOSTNAME] = {"ip_hostname", PF_INTERNAL | PF_BOOL},
83 #endif
84 [IP_MOUNT] = {"mount", PF_INTERNAL | PF_REV},
85 [IP_MOUNT_DEVFS] = {"mount.devfs", PF_INTERNAL | PF_BOOL},
86 [IP_MOUNT_FDESCFS] = {"mount.fdescfs", PF_INTERNAL | PF_BOOL},
87 [IP_MOUNT_PROCFS] = {"mount.procfs", PF_INTERNAL | PF_BOOL},
88 [IP_MOUNT_FSTAB] = {"mount.fstab", PF_INTERNAL},
89 [IP_STOP_TIMEOUT] = {"stop.timeout", PF_INTERNAL | PF_INT},
90 [IP_VNET_INTERFACE] = {"vnet.interface", PF_INTERNAL},
91 #ifdef INET
92 [IP__IP4_IFADDR] = {"ip4.addr", PF_INTERNAL | PF_CONV | PF_REV},
93 #endif
94 #ifdef INET6
95 [IP__IP6_IFADDR] = {"ip6.addr", PF_INTERNAL | PF_CONV | PF_REV},
96 #endif
97 [IP__MOUNT_FROM_FSTAB] = {"mount.fstab", PF_INTERNAL | PF_CONV | PF_REV},
98 [IP__OP] = {NULL, PF_CONV},
99 [KP_ALLOW_CHFLAGS] = {"allow.chflags", 0},
100 [KP_ALLOW_MOUNT] = {"allow.mount", 0},
101 [KP_ALLOW_RAW_SOCKETS] = {"allow.raw_sockets", 0},
102 [KP_ALLOW_SET_HOSTNAME]= {"allow.set_hostname", 0},
103 [KP_ALLOW_SOCKET_AF] = {"allow.socket_af", 0},
104 [KP_ALLOW_SYSVIPC] = {"allow.sysvipc", 0},
105 [KP_DEVFS_RULESET] = {"devfs_ruleset", 0},
106 [KP_ENFORCE_STATFS] = {"enforce_statfs", 0},
107 [KP_HOST_HOSTNAME] = {"host.hostname", 0},
108 #ifdef INET
109 [KP_IP4_ADDR] = {"ip4.addr", 0},
110 #endif
111 #ifdef INET6
112 [KP_IP6_ADDR] = {"ip6.addr", 0},
113 #endif
114 [KP_JID] = {"jid", PF_IMMUTABLE},
115 [KP_NAME] = {"name", PF_IMMUTABLE},
116 [KP_PATH] = {"path", 0},
117 [KP_PERSIST] = {"persist", 0},
118 [KP_SECURELEVEL] = {"securelevel", 0},
119 [KP_VNET] = {"vnet", 0},
120 };
121
122 /*
123 * Parse the jail configuration file.
124 */
125 void
load_config(void)126 load_config(void)
127 {
128 struct cfjails wild;
129 struct cfparams opp;
130 struct cfjail *j, *tj, *wj;
131 struct cfparam *p, *vp, *tp;
132 struct cfstring *s, *vs, *ns;
133 struct cfvar *v, *vv;
134 char *ep;
135 int did_self, jseq, pgen;
136
137 if (!strcmp(cfname, "-")) {
138 cfname = "STDIN";
139 yyin = stdin;
140 } else {
141 yyin = fopen(cfname, "r");
142 if (!yyin)
143 err(1, "%s", cfname);
144 }
145 if (yyparse() || yynerrs)
146 exit(1);
147
148 /* Separate the wildcard jails out from the actual jails. */
149 jseq = 0;
150 TAILQ_INIT(&wild);
151 TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
152 j->seq = ++jseq;
153 if (wild_jail_name(j->name))
154 requeue(j, &wild);
155 }
156
157 TAILQ_FOREACH(j, &cfjails, tq) {
158 /* Set aside the jail's parameters. */
159 TAILQ_INIT(&opp);
160 TAILQ_CONCAT(&opp, &j->params, tq);
161 /*
162 * The jail name implies its "name" or "jid" parameter,
163 * though they may also be explicitly set later on.
164 */
165 add_param(j, NULL,
166 strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
167 j->name);
168 /*
169 * Collect parameters for the jail, global parameters/variables,
170 * and any matching wildcard jails.
171 */
172 did_self = 0;
173 TAILQ_FOREACH(wj, &wild, tq) {
174 if (j->seq < wj->seq && !did_self) {
175 TAILQ_FOREACH(p, &opp, tq)
176 add_param(j, p, 0, NULL);
177 did_self = 1;
178 }
179 if (wild_jail_match(j->name, wj->name))
180 TAILQ_FOREACH(p, &wj->params, tq)
181 add_param(j, p, 0, NULL);
182 }
183 if (!did_self)
184 TAILQ_FOREACH(p, &opp, tq)
185 add_param(j, p, 0, NULL);
186
187 /* Resolve any variable substitutions. */
188 pgen = 0;
189 TAILQ_FOREACH(p, &j->params, tq) {
190 p->gen = ++pgen;
191 find_vars:
192 TAILQ_FOREACH(s, &p->val, tq) {
193 while ((v = STAILQ_FIRST(&s->vars))) {
194 TAILQ_FOREACH(vp, &j->params, tq)
195 if (!strcmp(vp->name, v->name))
196 break;
197 if (!vp) {
198 jail_warnx(j,
199 "%s: variable \"%s\" not found",
200 p->name, v->name);
201 bad_var:
202 j->flags |= JF_FAILED;
203 TAILQ_FOREACH(vp, &j->params, tq)
204 if (vp->gen == pgen)
205 vp->flags |= PF_BAD;
206 goto free_var;
207 }
208 if (vp->flags & PF_BAD)
209 goto bad_var;
210 if (vp->gen == pgen) {
211 jail_warnx(j, "%s: variable loop",
212 v->name);
213 goto bad_var;
214 }
215 TAILQ_FOREACH(vs, &vp->val, tq)
216 if (!STAILQ_EMPTY(&vs->vars)) {
217 vp->gen = pgen;
218 TAILQ_REMOVE(&j->params, vp,
219 tq);
220 TAILQ_INSERT_BEFORE(p, vp, tq);
221 p = vp;
222 goto find_vars;
223 }
224 vs = TAILQ_FIRST(&vp->val);
225 if (TAILQ_NEXT(vs, tq) != NULL &&
226 (s->s[0] != '\0' ||
227 STAILQ_NEXT(v, tq))) {
228 jail_warnx(j, "%s: array cannot be "
229 "substituted inline",
230 p->name);
231 goto bad_var;
232 }
233 s->s = erealloc(s->s, s->len + vs->len + 1);
234 memmove(s->s + v->pos + vs->len,
235 s->s + v->pos,
236 s->len - v->pos + 1);
237 memcpy(s->s + v->pos, vs->s, vs->len);
238 vv = v;
239 while ((vv = STAILQ_NEXT(vv, tq)))
240 vv->pos += vs->len;
241 s->len += vs->len;
242 while ((vs = TAILQ_NEXT(vs, tq))) {
243 ns = emalloc(sizeof(struct cfstring));
244 ns->s = estrdup(vs->s);
245 ns->len = vs->len;
246 STAILQ_INIT(&ns->vars);
247 TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
248 s = ns;
249 }
250 free_var:
251 free(v->name);
252 STAILQ_REMOVE_HEAD(&s->vars, tq);
253 free(v);
254 }
255 }
256 }
257
258 /* Free the jail's original parameter list and any variables. */
259 while ((p = TAILQ_FIRST(&opp)))
260 free_param(&opp, p);
261 TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
262 if (p->flags & PF_VAR)
263 free_param(&j->params, p);
264 }
265 while ((wj = TAILQ_FIRST(&wild))) {
266 free(wj->name);
267 while ((p = TAILQ_FIRST(&wj->params)))
268 free_param(&wj->params, p);
269 TAILQ_REMOVE(&wild, wj, tq);
270 }
271 }
272
273 /*
274 * Create a new jail record.
275 */
276 struct cfjail *
add_jail(void)277 add_jail(void)
278 {
279 struct cfjail *j;
280
281 j = emalloc(sizeof(struct cfjail));
282 memset(j, 0, sizeof(struct cfjail));
283 TAILQ_INIT(&j->params);
284 STAILQ_INIT(&j->dep[DEP_FROM]);
285 STAILQ_INIT(&j->dep[DEP_TO]);
286 j->queue = &cfjails;
287 TAILQ_INSERT_TAIL(&cfjails, j, tq);
288 return j;
289 }
290
291 /*
292 * Add a parameter to a jail.
293 */
294 void
add_param(struct cfjail * j,const struct cfparam * p,enum intparam ipnum,const char * value)295 add_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
296 const char *value)
297 {
298 struct cfstrings nss;
299 struct cfparam *dp, *np;
300 struct cfstring *s, *ns;
301 struct cfvar *v, *nv;
302 const char *name;
303 char *cs, *tname;
304 unsigned flags;
305
306 if (j == NULL) {
307 /* Create a single anonymous jail if one doesn't yet exist. */
308 j = TAILQ_LAST(&cfjails, cfjails);
309 if (j == NULL)
310 j = add_jail();
311 }
312 TAILQ_INIT(&nss);
313 if (p != NULL) {
314 name = p->name;
315 flags = p->flags;
316 /*
317 * Make a copy of the parameter's string list,
318 * which may be freed if it's overridden later.
319 */
320 TAILQ_FOREACH(s, &p->val, tq) {
321 ns = emalloc(sizeof(struct cfstring));
322 ns->s = estrdup(s->s);
323 ns->len = s->len;
324 STAILQ_INIT(&ns->vars);
325 STAILQ_FOREACH(v, &s->vars, tq) {
326 nv = emalloc(sizeof(struct cfvar));
327 nv->name = strdup(v->name);
328 nv->pos = v->pos;
329 STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
330 }
331 TAILQ_INSERT_TAIL(&nss, ns, tq);
332 }
333 } else {
334 flags = PF_APPEND;
335 if (ipnum != IP__NULL) {
336 name = intparams[ipnum].name;
337 flags |= intparams[ipnum].flags;
338 } else if ((cs = strchr(value, '='))) {
339 tname = alloca(cs - value + 1);
340 strlcpy(tname, value, cs - value + 1);
341 name = tname;
342 value = cs + 1;
343 } else {
344 name = value;
345 value = NULL;
346 }
347 if (value != NULL) {
348 ns = emalloc(sizeof(struct cfstring));
349 ns->s = estrdup(value);
350 ns->len = strlen(value);
351 STAILQ_INIT(&ns->vars);
352 TAILQ_INSERT_TAIL(&nss, ns, tq);
353 }
354 }
355
356 /* See if this parameter has already been added. */
357 if (ipnum != IP__NULL)
358 dp = j->intparams[ipnum];
359 else
360 TAILQ_FOREACH(dp, &j->params, tq)
361 if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
362 break;
363 if (dp != NULL) {
364 /* Found it - append or replace. */
365 if (dp->flags & PF_IMMUTABLE) {
366 jail_warnx(j, "cannot redefine variable \"%s\".",
367 dp->name);
368 return;
369 }
370 if (strcmp(dp->name, name)) {
371 free(dp->name);
372 dp->name = estrdup(name);
373 }
374 if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
375 free_param_strings(dp);
376 TAILQ_CONCAT(&dp->val, &nss, tq);
377 dp->flags |= flags;
378 } else {
379 /* Not found - add it. */
380 np = emalloc(sizeof(struct cfparam));
381 np->name = estrdup(name);
382 TAILQ_INIT(&np->val);
383 TAILQ_CONCAT(&np->val, &nss, tq);
384 np->flags = flags;
385 np->gen = 0;
386 TAILQ_INSERT_TAIL(&j->params, np, tq);
387 if (ipnum != IP__NULL)
388 j->intparams[ipnum] = np;
389 else
390 for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
391 if (!(intparams[ipnum].flags & PF_CONV) &&
392 equalopts(name, intparams[ipnum].name)) {
393 j->intparams[ipnum] = np;
394 np->flags |= intparams[ipnum].flags;
395 break;
396 }
397 }
398 }
399
400 /*
401 * Return if a boolean parameter exists and is true.
402 */
403 int
bool_param(const struct cfparam * p)404 bool_param(const struct cfparam *p)
405 {
406 const char *cs;
407
408 if (p == NULL)
409 return 0;
410 cs = strrchr(p->name, '.');
411 return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
412 (TAILQ_EMPTY(&p->val) ||
413 !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
414 (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
415 }
416
417 /*
418 * Set an integer if a parameter if it exists.
419 */
420 int
int_param(const struct cfparam * p,int * ip)421 int_param(const struct cfparam *p, int *ip)
422 {
423 if (p == NULL || TAILQ_EMPTY(&p->val))
424 return 0;
425 *ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
426 return 1;
427 }
428
429 /*
430 * Return the string value of a scalar parameter if it exists.
431 */
432 const char *
string_param(const struct cfparam * p)433 string_param(const struct cfparam *p)
434 {
435 return (p && !TAILQ_EMPTY(&p->val)
436 ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
437 }
438
439 /*
440 * Check syntax and values of internal parameters. Set some internal
441 * parameters based on the values of others.
442 */
443 int
check_intparams(struct cfjail * j)444 check_intparams(struct cfjail *j)
445 {
446 struct cfparam *p;
447 struct cfstring *s;
448 FILE *f;
449 const char *val;
450 char *cs, *ep, *ln;
451 size_t lnlen;
452 int error;
453 #if defined(INET) || defined(INET6)
454 struct addrinfo hints;
455 struct addrinfo *ai0, *ai;
456 const char *hostname;
457 int gicode, defif, prefix;
458 #endif
459 #ifdef INET
460 struct in_addr addr4;
461 int ip4ok;
462 char avalue4[INET_ADDRSTRLEN];
463 #endif
464 #ifdef INET6
465 struct in6_addr addr6;
466 int ip6ok;
467 char avalue6[INET6_ADDRSTRLEN];
468 #endif
469
470 error = 0;
471 /* Check format of boolan and integer values. */
472 TAILQ_FOREACH(p, &j->params, tq) {
473 if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
474 val = TAILQ_LAST(&p->val, cfstrings)->s;
475 if (p->flags & PF_BOOL) {
476 if (strcasecmp(val, "false") &&
477 strcasecmp(val, "true") &&
478 ((void)strtol(val, &ep, 10), *ep)) {
479 jail_warnx(j,
480 "%s: unknown boolean value \"%s\"",
481 p->name, val);
482 error = -1;
483 }
484 } else {
485 (void)strtol(val, &ep, 10);
486 if (ep == val || *ep) {
487 jail_warnx(j,
488 "%s: non-integer value \"%s\"",
489 p->name, val);
490 error = -1;
491 }
492 }
493 }
494 }
495
496 #if defined(INET) || defined(INET6)
497 /*
498 * The ip_hostname parameter looks up the hostname, and adds parameters
499 * for any IP addresses it finds.
500 */
501 if (((j->flags & JF_OP_MASK) != JF_STOP ||
502 j->intparams[IP_INTERFACE] != NULL) &&
503 bool_param(j->intparams[IP_IP_HOSTNAME]) &&
504 (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
505 j->intparams[IP_IP_HOSTNAME] = NULL;
506 /*
507 * Silently ignore unsupported address families from
508 * DNS lookups.
509 */
510 #ifdef INET
511 ip4ok = feature_present("inet");
512 #endif
513 #ifdef INET6
514 ip6ok = feature_present("inet6");
515 #endif
516 if (
517 #if defined(INET) && defined(INET6)
518 ip4ok || ip6ok
519 #elif defined(INET)
520 ip4ok
521 #elif defined(INET6)
522 ip6ok
523 #endif
524 ) {
525 /* Look up the hostname (or get the address) */
526 memset(&hints, 0, sizeof(hints));
527 hints.ai_socktype = SOCK_STREAM;
528 hints.ai_family =
529 #if defined(INET) && defined(INET6)
530 ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) : PF_INET6;
531 #elif defined(INET)
532 PF_INET;
533 #elif defined(INET6)
534 PF_INET6;
535 #endif
536 gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
537 if (gicode != 0) {
538 jail_warnx(j, "host.hostname %s: %s", hostname,
539 gai_strerror(gicode));
540 error = -1;
541 } else {
542 /*
543 * Convert the addresses to ASCII so jailparam
544 * can convert them back. Errors are not
545 * expected here.
546 */
547 for (ai = ai0; ai; ai = ai->ai_next)
548 switch (ai->ai_family) {
549 #ifdef INET
550 case AF_INET:
551 memcpy(&addr4,
552 &((struct sockaddr_in *)
553 (void *)ai->ai_addr)->
554 sin_addr, sizeof(addr4));
555 if (inet_ntop(AF_INET,
556 &addr4, avalue4,
557 INET_ADDRSTRLEN) == NULL)
558 err(1, "inet_ntop");
559 add_param(j, NULL, KP_IP4_ADDR,
560 avalue4);
561 break;
562 #endif
563 #ifdef INET6
564 case AF_INET6:
565 memcpy(&addr6,
566 &((struct sockaddr_in6 *)
567 (void *)ai->ai_addr)->
568 sin6_addr, sizeof(addr6));
569 if (inet_ntop(AF_INET6,
570 &addr6, avalue6,
571 INET6_ADDRSTRLEN) == NULL)
572 err(1, "inet_ntop");
573 add_param(j, NULL, KP_IP6_ADDR,
574 avalue6);
575 break;
576 #endif
577 }
578 freeaddrinfo(ai0);
579 }
580 }
581 }
582
583 /*
584 * IP addresses may include an interface to set that address on,
585 * a netmask/suffix for that address and options for ifconfig.
586 * These are copied to an internal command parameter and then stripped
587 * so they won't be passed on to jailparam_set.
588 */
589 defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
590 #ifdef INET
591 if (j->intparams[KP_IP4_ADDR] != NULL) {
592 TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
593 cs = strchr(s->s, '|');
594 if (cs || defif)
595 add_param(j, NULL, IP__IP4_IFADDR, s->s);
596 if (cs) {
597 strcpy(s->s, cs + 1);
598 s->len -= cs + 1 - s->s;
599 }
600 if ((cs = strchr(s->s, '/'))) {
601 prefix = strtol(cs + 1, &ep, 10);
602 if (*ep == '.'
603 ? inet_pton(AF_INET, cs + 1, &addr4) != 1
604 : *ep || prefix < 0 || prefix > 32) {
605 jail_warnx(j,
606 "ip4.addr: bad netmask \"%s\"", cs);
607 error = -1;
608 }
609 *cs = '\0';
610 s->len = cs - s->s;
611 }
612 if ((cs = strchr(s->s, ' ')) != NULL) {
613 *cs = '\0';
614 s->len = cs - s->s;
615 }
616 }
617 }
618 #endif
619 #ifdef INET6
620 if (j->intparams[KP_IP6_ADDR] != NULL) {
621 TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
622 cs = strchr(s->s, '|');
623 if (cs || defif)
624 add_param(j, NULL, IP__IP6_IFADDR, s->s);
625 if (cs) {
626 strcpy(s->s, cs + 1);
627 s->len -= cs + 1 - s->s;
628 }
629 if ((cs = strchr(s->s, '/'))) {
630 prefix = strtol(cs + 1, &ep, 10);
631 if (*ep || prefix < 0 || prefix > 128) {
632 jail_warnx(j,
633 "ip6.addr: bad prefixlen \"%s\"",
634 cs);
635 error = -1;
636 }
637 *cs = '\0';
638 s->len = cs - s->s;
639 }
640 if ((cs = strchr(s->s, ' ')) != NULL) {
641 *cs = '\0';
642 s->len = cs - s->s;
643 }
644 }
645 }
646 #endif
647 #endif
648
649 /*
650 * Read mount.fstab file(s), and treat each line as its own mount
651 * parameter.
652 */
653 if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
654 TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
655 if (s->len == 0)
656 continue;
657 f = fopen(s->s, "r");
658 if (f == NULL) {
659 jail_warnx(j, "mount.fstab: %s: %s",
660 s->s, strerror(errno));
661 error = -1;
662 continue;
663 }
664 while ((ln = fgetln(f, &lnlen))) {
665 if ((cs = memchr(ln, '#', lnlen - 1)))
666 lnlen = cs - ln + 1;
667 if (ln[lnlen - 1] == '\n' ||
668 ln[lnlen - 1] == '#')
669 ln[lnlen - 1] = '\0';
670 else {
671 cs = alloca(lnlen + 1);
672 strlcpy(cs, ln, lnlen + 1);
673 ln = cs;
674 }
675 add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
676 }
677 fclose(f);
678 }
679 }
680 if (error)
681 failed(j);
682 return error;
683 }
684
685 /*
686 * Import parameters into libjail's binary jailparam format.
687 */
688 int
import_params(struct cfjail * j)689 import_params(struct cfjail *j)
690 {
691 struct cfparam *p;
692 struct cfstring *s, *ts;
693 struct jailparam *jp;
694 char *value, *cs;
695 size_t vallen;
696 int error;
697
698 error = 0;
699 j->njp = 0;
700 TAILQ_FOREACH(p, &j->params, tq)
701 if (!(p->flags & PF_INTERNAL))
702 j->njp++;
703 j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
704 TAILQ_FOREACH(p, &j->params, tq) {
705 if (p->flags & PF_INTERNAL)
706 continue;
707 if (jailparam_init(jp, p->name) < 0) {
708 error = -1;
709 jail_warnx(j, "%s", jail_errmsg);
710 jp++;
711 continue;
712 }
713 if (TAILQ_EMPTY(&p->val))
714 value = NULL;
715 else if (!jp->jp_elemlen ||
716 !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
717 /*
718 * Scalar parameters silently discard multiple (array)
719 * values, keeping only the last value added. This
720 * lets values added from the command line append to
721 * arrays wthout pre-checking the type.
722 */
723 value = TAILQ_LAST(&p->val, cfstrings)->s;
724 } else {
725 /*
726 * Convert arrays into comma-separated strings, which
727 * jailparam_import will then convert back into arrays.
728 */
729 vallen = 0;
730 TAILQ_FOREACH(s, &p->val, tq)
731 vallen += s->len + 1;
732 value = alloca(vallen);
733 cs = value;
734 TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
735 memcpy(cs, s->s, s->len);
736 cs += s->len + 1;
737 cs[-1] = ',';
738 }
739 value[vallen - 1] = '\0';
740 }
741 if (jailparam_import(jp, value) < 0) {
742 error = -1;
743 jail_warnx(j, "%s", jail_errmsg);
744 }
745 jp++;
746 }
747 if (error) {
748 jailparam_free(j->jp, j->njp);
749 free(j->jp);
750 j->jp = NULL;
751 failed(j);
752 }
753 return error;
754 }
755
756 /*
757 * Check if options are equal (with or without the "no" prefix).
758 */
759 int
equalopts(const char * opt1,const char * opt2)760 equalopts(const char *opt1, const char *opt2)
761 {
762 char *p;
763
764 /* "opt" vs. "opt" or "noopt" vs. "noopt" */
765 if (strcmp(opt1, opt2) == 0)
766 return (1);
767 /* "noopt" vs. "opt" */
768 if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
769 return (1);
770 /* "opt" vs. "noopt" */
771 if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
772 return (1);
773 while ((p = strchr(opt1, '.')) != NULL &&
774 !strncmp(opt1, opt2, ++p - opt1)) {
775 opt2 += p - opt1;
776 opt1 = p;
777 /* "foo.noopt" vs. "foo.opt" */
778 if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
779 return (1);
780 /* "foo.opt" vs. "foo.noopt" */
781 if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
782 return (1);
783 }
784 return (0);
785 }
786
787 /*
788 * See if a jail name matches a wildcard.
789 */
790 int
wild_jail_match(const char * jname,const char * wname)791 wild_jail_match(const char *jname, const char *wname)
792 {
793 const char *jc, *jd, *wc, *wd;
794
795 /*
796 * A non-final "*" component in the wild name matches a single jail
797 * component, and a final "*" matches one or more jail components.
798 */
799 for (jc = jname, wc = wname;
800 (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
801 jc = jd + 1, wc = wd + 1)
802 if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
803 return 0;
804 return (!strcmp(jc, wc) || !strcmp(wc, "*"));
805 }
806
807 /*
808 * Return if a jail name is a wildcard.
809 */
810 int
wild_jail_name(const char * wname)811 wild_jail_name(const char *wname)
812 {
813 const char *wc;
814
815 for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
816 if ((wc == wname || wc[-1] == '.') &&
817 (wc[1] == '\0' || wc[1] == '.'))
818 return 1;
819 return 0;
820 }
821
822 /*
823 * Free a parameter record and all its strings and variables.
824 */
825 static void
free_param(struct cfparams * pp,struct cfparam * p)826 free_param(struct cfparams *pp, struct cfparam *p)
827 {
828 free(p->name);
829 free_param_strings(p);
830 TAILQ_REMOVE(pp, p, tq);
831 free(p);
832 }
833
834 static void
free_param_strings(struct cfparam * p)835 free_param_strings(struct cfparam *p)
836 {
837 struct cfstring *s;
838 struct cfvar *v;
839
840 while ((s = TAILQ_FIRST(&p->val))) {
841 free(s->s);
842 while ((v = STAILQ_FIRST(&s->vars))) {
843 free(v->name);
844 STAILQ_REMOVE_HEAD(&s->vars, tq);
845 free(v);
846 }
847 TAILQ_REMOVE(&p->val, s, tq);
848 free(s);
849 }
850 }
851