]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/jail/config.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / jail / config.c
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_FSTAB] =          {"mount.fstab",         PF_INTERNAL},
88     [IP_STOP_TIMEOUT] =         {"stop.timeout",        PF_INTERNAL | PF_INT},
89     [IP_VNET_INTERFACE] =       {"vnet.interface",      PF_INTERNAL},
90 #ifdef INET
91     [IP__IP4_IFADDR] =          {"ip4.addr",    PF_INTERNAL | PF_CONV | PF_REV},
92 #endif
93 #ifdef INET6
94     [IP__IP6_IFADDR] =          {"ip6.addr",    PF_INTERNAL | PF_CONV | PF_REV},
95 #endif
96     [IP__MOUNT_FROM_FSTAB] =    {"mount.fstab", PF_INTERNAL | PF_CONV | PF_REV},
97     [IP__OP] =                  {NULL,                  PF_CONV},
98     [KP_ALLOW_CHFLAGS] =        {"allow.chflags",       0},
99     [KP_ALLOW_MOUNT] =          {"allow.mount",         0},
100     [KP_ALLOW_RAW_SOCKETS] =    {"allow.raw_sockets",   0},
101     [KP_ALLOW_SET_HOSTNAME]=    {"allow.set_hostname",  0},
102     [KP_ALLOW_SOCKET_AF] =      {"allow.socket_af",     0},
103     [KP_ALLOW_SYSVIPC] =        {"allow.sysvipc",       0},
104     [KP_DEVFS_RULESET] =        {"devfs_ruleset",       0},
105     [KP_ENFORCE_STATFS] =       {"enforce_statfs",      0},
106     [KP_HOST_HOSTNAME] =        {"host.hostname",       0},
107 #ifdef INET
108     [KP_IP4_ADDR] =             {"ip4.addr",            0},
109 #endif
110 #ifdef INET6
111     [KP_IP6_ADDR] =             {"ip6.addr",            0},
112 #endif
113     [KP_JID] =                  {"jid",                 0},
114     [KP_NAME] =                 {"name",                0},
115     [KP_PATH] =                 {"path",                0},
116     [KP_PERSIST] =              {"persist",             0},
117     [KP_SECURELEVEL] =          {"securelevel",         0},
118     [KP_VNET] =                 {"vnet",                0},
119 };
120
121 /*
122  * Parse the jail configuration file.
123  */
124 void
125 load_config(void)
126 {
127         struct cfjails wild;
128         struct cfparams opp;
129         struct cfjail *j, *tj, *wj;
130         struct cfparam *p, *vp, *tp;
131         struct cfstring *s, *vs, *ns;
132         struct cfvar *v;
133         char *ep;
134         size_t varoff;
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                         varoff = 0;
194                         while ((v = STAILQ_FIRST(&s->vars))) {
195                                 TAILQ_FOREACH(vp, &j->params, tq)
196                                         if (!strcmp(vp->name, v->name))
197                                                 break;
198                                 if (!vp) {
199                                         jail_warnx(j,
200                                             "%s: variable \"%s\" not found",
201                                             p->name, v->name);
202                                 bad_var:
203                                         j->flags |= JF_FAILED;
204                                         TAILQ_FOREACH(vp, &j->params, tq)
205                                                 if (vp->gen == pgen)
206                                                         vp->flags |= PF_BAD;
207                                         goto free_var;
208                                 }
209                                 if (vp->flags & PF_BAD)
210                                         goto bad_var;
211                                 if (vp->gen == pgen) {
212                                         jail_warnx(j, "%s: variable loop",
213                                             v->name);
214                                         goto bad_var;
215                                 }
216                                 TAILQ_FOREACH(vs, &vp->val, tq)
217                                         if (!STAILQ_EMPTY(&vs->vars)) {
218                                                 vp->gen = pgen;
219                                                 TAILQ_REMOVE(&j->params, vp,
220                                                     tq);
221                                                 TAILQ_INSERT_BEFORE(p, vp, tq);
222                                                 p = vp;
223                                                 goto find_vars;
224                                         }
225                                 vs = TAILQ_FIRST(&vp->val);
226                                 if (TAILQ_NEXT(vs, tq) != NULL &&
227                                     (s->s[0] != '\0' ||
228                                      STAILQ_NEXT(v, tq))) {
229                                         jail_warnx(j, "%s: array cannot be "
230                                             "substituted inline",
231                                             p->name);
232                                         goto bad_var;
233                                 }
234                                 s->s = erealloc(s->s, s->len + vs->len + 1);
235                                 memmove(s->s + v->pos + varoff + vs->len,
236                                     s->s + v->pos + varoff,
237                                     s->len - (v->pos + varoff) + 1);
238                                 memcpy(s->s + v->pos + varoff, vs->s, vs->len);
239                                 varoff += vs->len;
240                                 s->len += vs->len;
241                                 while ((vs = TAILQ_NEXT(vs, tq))) {
242                                         ns = emalloc(sizeof(struct cfstring));
243                                         ns->s = estrdup(vs->s);
244                                         ns->len = vs->len;
245                                         STAILQ_INIT(&ns->vars);
246                                         TAILQ_INSERT_AFTER(&p->val, s, ns, tq);
247                                         s = ns;
248                                 }
249                         free_var:
250                                 free(v->name);
251                                 STAILQ_REMOVE_HEAD(&s->vars, tq);
252                                 free(v);
253                         }
254                     }
255                 }
256
257                 /* Free the jail's original parameter list and any variables. */
258                 while ((p = TAILQ_FIRST(&opp)))
259                         free_param(&opp, p);
260                 TAILQ_FOREACH_SAFE(p, &j->params, tq, tp)
261                         if (p->flags & PF_VAR)
262                                 free_param(&j->params, p);
263         }
264         while ((wj = TAILQ_FIRST(&wild))) {
265                 free(wj->name);
266                 while ((p = TAILQ_FIRST(&wj->params)))
267                         free_param(&wj->params, p);
268                 TAILQ_REMOVE(&wild, wj, tq);
269         }
270 }
271
272 /*
273  * Create a new jail record.
274  */
275 struct cfjail *
276 add_jail(void)
277 {
278         struct cfjail *j;
279
280         j = emalloc(sizeof(struct cfjail));
281         memset(j, 0, sizeof(struct cfjail));
282         TAILQ_INIT(&j->params);
283         STAILQ_INIT(&j->dep[DEP_FROM]);
284         STAILQ_INIT(&j->dep[DEP_TO]);
285         j->queue = &cfjails;
286         TAILQ_INSERT_TAIL(&cfjails, j, tq);
287         return j;
288 }
289
290 /*
291  * Add a parameter to a jail.
292  */
293 void
294 add_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum,
295     const char *value)
296 {
297         struct cfstrings nss;
298         struct cfparam *dp, *np;
299         struct cfstring *s, *ns;
300         struct cfvar *v, *nv;
301         const char *name;
302         char *cs, *tname;
303         unsigned flags;
304
305         if (j == NULL) {
306                 /* Create a single anonymous jail if one doesn't yet exist. */
307                 j = TAILQ_LAST(&cfjails, cfjails);
308                 if (j == NULL)
309                         j = add_jail();
310         }
311         TAILQ_INIT(&nss);
312         if (p != NULL) {
313                 name = p->name;
314                 flags = p->flags;
315                 /*
316                  * Make a copy of the parameter's string list,
317                  * which may be freed if it's overridden later.
318                  */
319                 TAILQ_FOREACH(s, &p->val, tq) {
320                         ns = emalloc(sizeof(struct cfstring));
321                         ns->s = estrdup(s->s);
322                         ns->len = s->len;
323                         STAILQ_INIT(&ns->vars);
324                         STAILQ_FOREACH(v, &s->vars, tq) {
325                                 nv = emalloc(sizeof(struct cfvar));
326                                 nv->name = strdup(v->name);
327                                 nv->pos = v->pos;
328                                 STAILQ_INSERT_TAIL(&ns->vars, nv, tq);
329                         }
330                         TAILQ_INSERT_TAIL(&nss, ns, tq);
331                 }
332         } else {
333                 flags = PF_APPEND;
334                 if (ipnum != IP__NULL) {
335                         name = intparams[ipnum].name;
336                         flags |= intparams[ipnum].flags;
337                 } else if ((cs = strchr(value, '='))) {
338                         tname = alloca(cs - value + 1);
339                         strlcpy(tname, value, cs - value + 1);
340                         name = tname;
341                         value = cs + 1;
342                 } else {
343                         name = value;
344                         value = NULL;
345                 }
346                 if (value != NULL) {
347                         ns = emalloc(sizeof(struct cfstring));
348                         ns->s = estrdup(value);
349                         ns->len = strlen(value);
350                         STAILQ_INIT(&ns->vars);
351                         TAILQ_INSERT_TAIL(&nss, ns, tq);
352                 }
353         }
354
355         /* See if this parameter has already been added. */
356         if (ipnum != IP__NULL)
357                 dp = j->intparams[ipnum];
358         else
359                 TAILQ_FOREACH(dp, &j->params, tq)
360                         if (!(dp->flags & PF_CONV) && equalopts(dp->name, name))
361                                 break;
362         if (dp != NULL) {
363                 /* Found it - append or replace. */
364                 if (strcmp(dp->name, name)) {
365                         free(dp->name);
366                         dp->name = estrdup(name);
367                 }
368                 if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
369                         free_param_strings(dp);
370                 TAILQ_CONCAT(&dp->val, &nss, tq);
371                 dp->flags |= flags;
372         } else {
373                 /* Not found - add it. */
374                 np = emalloc(sizeof(struct cfparam));
375                 np->name = estrdup(name);
376                 TAILQ_INIT(&np->val);
377                 TAILQ_CONCAT(&np->val, &nss, tq);
378                 np->flags = flags;
379                 np->gen = 0;
380                 TAILQ_INSERT_TAIL(&j->params, np, tq);
381                 if (ipnum != IP__NULL)
382                         j->intparams[ipnum] = np;
383                 else
384                         for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
385                                 if (!(intparams[ipnum].flags & PF_CONV) &&
386                                     equalopts(name, intparams[ipnum].name)) {
387                                         j->intparams[ipnum] = np;
388                                         np->flags |= intparams[ipnum].flags;
389                                         break;
390                                 }
391         }
392 }
393
394 /*
395  * Return if a boolean parameter exists and is true.
396  */
397 int
398 bool_param(const struct cfparam *p)
399 {
400         const char *cs;
401
402         if (p == NULL)
403                 return 0;
404         cs = strrchr(p->name, '.');
405         return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
406             (TAILQ_EMPTY(&p->val) ||
407              !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
408              (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
409 }
410
411 /*
412  * Set an integer if a parameter if it exists.
413  */
414 int
415 int_param(const struct cfparam *p, int *ip)
416 {
417         if (p == NULL || TAILQ_EMPTY(&p->val))
418                 return 0;
419         *ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
420         return 1;
421 }
422
423 /*
424  * Return the string value of a scalar parameter if it exists.
425  */
426 const char *
427 string_param(const struct cfparam *p)
428 {
429         return (p && !TAILQ_EMPTY(&p->val)
430             ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
431 }
432
433 /*
434  * Check syntax and values of internal parameters.  Set some internal
435  * parameters based on the values of others.
436  */
437 int
438 check_intparams(struct cfjail *j)
439 {
440         struct cfparam *p;
441         struct cfstring *s;
442         FILE *f;
443         const char *val;
444         char *cs, *ep, *ln;
445         size_t lnlen;
446         int error;
447 #if defined(INET) || defined(INET6)
448         struct addrinfo hints;
449         struct addrinfo *ai0, *ai;
450         const char *hostname;
451         int gicode, defif, prefix;
452 #endif
453 #ifdef INET
454         struct in_addr addr4;
455         int ip4ok;
456         char avalue4[INET_ADDRSTRLEN];
457 #endif
458 #ifdef INET6
459         struct in6_addr addr6;
460         int ip6ok;
461         char avalue6[INET6_ADDRSTRLEN];
462 #endif
463
464         error = 0;
465         /* Check format of boolan and integer values. */
466         TAILQ_FOREACH(p, &j->params, tq) {
467                 if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
468                         val = TAILQ_LAST(&p->val, cfstrings)->s;
469                         if (p->flags & PF_BOOL) {
470                                 if (strcasecmp(val, "false") &&
471                                     strcasecmp(val, "true") &&
472                                     ((void)strtol(val, &ep, 10), *ep)) {
473                                         jail_warnx(j,
474                                             "%s: unknown boolean value \"%s\"",
475                                             p->name, val);
476                                         error = -1;
477                                 }
478                         } else {
479                                 (void)strtol(val, &ep, 10);
480                                 if (ep == val || *ep) {
481                                         jail_warnx(j,
482                                             "%s: non-integer value \"%s\"",
483                                             p->name, val);
484                                         error = -1;
485                                 }
486                         }
487                 }
488         }
489
490 #if defined(INET) || defined(INET6)
491         /*
492          * The ip_hostname parameter looks up the hostname, and adds parameters
493          * for any IP addresses it finds.
494          */
495         if (((j->flags & JF_OP_MASK) != JF_STOP ||
496             j->intparams[IP_INTERFACE] != NULL) &&
497             bool_param(j->intparams[IP_IP_HOSTNAME]) &&
498             (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
499                 j->intparams[IP_IP_HOSTNAME] = NULL;
500                 /*
501                  * Silently ignore unsupported address families from
502                  * DNS lookups.
503                  */
504 #ifdef INET
505                 ip4ok = feature_present("inet");
506 #endif
507 #ifdef INET6
508                 ip6ok = feature_present("inet6");
509 #endif
510                 if (
511 #if defined(INET) && defined(INET6)
512                     ip4ok || ip6ok
513 #elif defined(INET)
514                     ip4ok
515 #elif defined(INET6)
516                     ip6ok
517 #endif
518                          ) {
519                         /* Look up the hostname (or get the address) */
520                         memset(&hints, 0, sizeof(hints));
521                         hints.ai_socktype = SOCK_STREAM;
522                         hints.ai_family =
523 #if defined(INET) && defined(INET6)
524                             ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
525 #elif defined(INET)
526                             PF_INET;
527 #elif defined(INET6)
528                             PF_INET6;
529 #endif
530                         gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
531                         if (gicode != 0) {
532                                 jail_warnx(j, "host.hostname %s: %s", hostname,
533                                     gai_strerror(gicode));
534                                 error = -1;
535                         } else {
536                                 /*
537                                  * Convert the addresses to ASCII so jailparam
538                                  * can convert them back.  Errors are not
539                                  * expected here.
540                                  */
541                                 for (ai = ai0; ai; ai = ai->ai_next)
542                                         switch (ai->ai_family) {
543 #ifdef INET
544                                         case AF_INET:
545                                                 memcpy(&addr4,
546                                                     &((struct sockaddr_in *)
547                                                     (void *)ai->ai_addr)->
548                                                     sin_addr, sizeof(addr4));
549                                                 if (inet_ntop(AF_INET,
550                                                     &addr4, avalue4,
551                                                     INET_ADDRSTRLEN) == NULL)
552                                                         err(1, "inet_ntop");
553                                                 add_param(j, NULL, KP_IP4_ADDR,
554                                                     avalue4);
555                                                 break;
556 #endif
557 #ifdef INET6
558                                         case AF_INET6:
559                                                 memcpy(&addr6,
560                                                     &((struct sockaddr_in6 *)
561                                                     (void *)ai->ai_addr)->
562                                                     sin6_addr, sizeof(addr6));
563                                                 if (inet_ntop(AF_INET6,
564                                                     &addr6, avalue6,
565                                                     INET6_ADDRSTRLEN) == NULL)
566                                                         err(1, "inet_ntop");
567                                                 add_param(j, NULL, KP_IP6_ADDR,
568                                                     avalue6);
569                                                 break;
570 #endif
571                                         }
572                                 freeaddrinfo(ai0);
573                         }
574                 }
575         }
576
577         /*
578          * IP addresses may include an interface to set that address on,
579          * and a netmask/suffix for that address.
580          */
581         defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
582 #ifdef INET
583         if (j->intparams[KP_IP4_ADDR] != NULL) {
584                 TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
585                         cs = strchr(s->s, '|');
586                         if (cs || defif)
587                                 add_param(j, NULL, IP__IP4_IFADDR, s->s);
588                         if (cs) {
589                                 strcpy(s->s, cs + 1);
590                                 s->len -= cs + 1 - s->s;
591                         }
592                         if ((cs = strchr(s->s, '/'))) {
593                                 prefix = strtol(cs + 1, &ep, 10);
594                                 if (*ep == '.'
595                                     ? inet_pton(AF_INET, cs + 1, &addr4) != 1
596                                     : *ep || prefix < 0 || prefix > 32) {
597                                         jail_warnx(j,
598                                             "ip4.addr: bad netmask \"%s\"", cs);
599                                         error = -1;     
600                                 }
601                                 *cs = '\0';
602                                 s->len = cs - s->s;
603                         }
604                 }
605         }
606 #endif
607 #ifdef INET6
608         if (j->intparams[KP_IP6_ADDR] != NULL) {
609                 TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
610                         cs = strchr(s->s, '|');
611                         if (cs || defif)
612                                 add_param(j, NULL, IP__IP6_IFADDR, s->s);
613                         if (cs) {
614                                 strcpy(s->s, cs + 1);
615                                 s->len -= cs + 1 - s->s;
616                         }
617                         if ((cs = strchr(s->s, '/'))) {
618                                 prefix = strtol(cs + 1, &ep, 10);
619                                 if (*ep || prefix < 0 || prefix > 128) {
620                                         jail_warnx(j,
621                                             "ip6.addr: bad prefixlen \"%s\"",
622                                             cs);
623                                         error = -1;     
624                                 }
625                                 *cs = '\0';
626                                 s->len = cs - s->s;
627                         }
628                 }
629         }
630 #endif
631 #endif
632
633         /*
634          * Read mount.fstab file(s), and treat each line as its own mount
635          * parameter.
636          */
637         if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
638                 TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
639                         if (s->len == 0)
640                                 continue;
641                         f = fopen(s->s, "r");
642                         if (f == NULL) {
643                                 jail_warnx(j, "mount.fstab: %s: %s",
644                                     s->s, strerror(errno));
645                                 error = -1;
646                                 continue;
647                         }
648                         while ((ln = fgetln(f, &lnlen))) {
649                                 if ((cs = memchr(ln, '#', lnlen - 1)))
650                                         lnlen = cs - ln + 1;
651                                 if (ln[lnlen - 1] == '\n' ||
652                                     ln[lnlen - 1] == '#')
653                                         ln[lnlen - 1] = '\0';
654                                 else {
655                                         cs = alloca(lnlen + 1);
656                                         strlcpy(cs, ln, lnlen + 1);
657                                         ln = cs;
658                                 }
659                                 add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
660                         }
661                         fclose(f);
662                 }
663         }
664         if (error)
665                 failed(j);
666         return error;
667 }
668
669 /*
670  * Import parameters into libjail's binary jailparam format.
671  */
672 int
673 import_params(struct cfjail *j)
674 {
675         struct cfparam *p;
676         struct cfstring *s, *ts;
677         struct jailparam *jp;
678         char *value, *cs;
679         size_t vallen;
680         int error;
681
682         error = 0;
683         j->njp = 0;
684         TAILQ_FOREACH(p, &j->params, tq)
685                 if (!(p->flags & PF_INTERNAL))
686                         j->njp++;
687         j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
688         TAILQ_FOREACH(p, &j->params, tq) {
689                 if (p->flags & PF_INTERNAL)
690                         continue;
691                 if (jailparam_init(jp, p->name) < 0) {
692                         error = -1;
693                         jail_warnx(j, "%s", jail_errmsg);
694                         jp++;
695                         continue;
696                 }
697                 if (TAILQ_EMPTY(&p->val))
698                         value = NULL;
699                 else if (!jp->jp_elemlen ||
700                          !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
701                         /*
702                          * Scalar parameters silently discard multiple (array)
703                          * values, keeping only the last value added.  This
704                          * lets values added from the command line append to
705                          * arrays wthout pre-checking the type.
706                          */
707                         value = TAILQ_LAST(&p->val, cfstrings)->s;
708                 } else {
709                         /*
710                          * Convert arrays into comma-separated strings, which
711                          * jailparam_import will then convert back into arrays.
712                          */
713                         vallen = 0;
714                         TAILQ_FOREACH(s, &p->val, tq)
715                                 vallen += s->len + 1;
716                         value = alloca(vallen);
717                         cs = value;
718                         TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
719                                 memcpy(cs, s->s, s->len);
720                                 cs += s->len + 1;
721                                 cs[-1] = ',';
722                         }
723                         value[vallen - 1] = '\0';
724                 }
725                 if (jailparam_import(jp, value) < 0) {
726                         error = -1;
727                         jail_warnx(j, "%s", jail_errmsg);
728                 }
729                 jp++;
730         }
731         if (error) {
732                 jailparam_free(j->jp, j->njp);
733                 free(j->jp);
734                 j->jp = NULL;
735                 failed(j);
736         }
737         return error;
738 }
739
740 /*
741  * Check if options are equal (with or without the "no" prefix).
742  */
743 int
744 equalopts(const char *opt1, const char *opt2)
745 {
746         char *p;
747
748         /* "opt" vs. "opt" or "noopt" vs. "noopt" */
749         if (strcmp(opt1, opt2) == 0)
750                 return (1);
751         /* "noopt" vs. "opt" */
752         if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
753                 return (1);
754         /* "opt" vs. "noopt" */
755         if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
756                 return (1);
757         while ((p = strchr(opt1, '.')) != NULL &&
758             !strncmp(opt1, opt2, ++p - opt1)) {
759                 opt2 += p - opt1;
760                 opt1 = p;
761                 /* "foo.noopt" vs. "foo.opt" */
762                 if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
763                         return (1);
764                 /* "foo.opt" vs. "foo.noopt" */
765                 if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
766                         return (1);
767         }
768         return (0);
769 }
770
771 /*
772  * See if a jail name matches a wildcard.
773  */
774 int
775 wild_jail_match(const char *jname, const char *wname)
776 {
777         const char *jc, *jd, *wc, *wd;
778
779         /*
780          * A non-final "*" component in the wild name matches a single jail
781          * component, and a final "*" matches one or more jail components.
782          */
783         for (jc = jname, wc = wname;
784              (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
785              jc = jd + 1, wc = wd + 1)
786                 if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
787                         return 0;
788         return (!strcmp(jc, wc) || !strcmp(wc, "*"));
789 }
790
791 /*
792  * Return if a jail name is a wildcard.
793  */
794 int
795 wild_jail_name(const char *wname)
796 {
797         const char *wc;
798
799         for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
800                 if ((wc == wname || wc[-1] == '.') &&
801                     (wc[1] == '\0' || wc[1] == '.'))
802                         return 1;
803         return 0;
804 }
805
806 /*
807  * Free a parameter record and all its strings and variables.
808  */
809 static void
810 free_param(struct cfparams *pp, struct cfparam *p)
811 {
812         free(p->name);
813         free_param_strings(p);
814         TAILQ_REMOVE(pp, p, tq);
815         free(p);
816 }
817
818 static void
819 free_param_strings(struct cfparam *p)
820 {
821         struct cfstring *s;
822         struct cfvar *v;
823
824         while ((s = TAILQ_FIRST(&p->val))) {
825                 free(s->s);
826                 while ((v = STAILQ_FIRST(&s->vars))) {
827                         free(v->name);
828                         STAILQ_REMOVE_HEAD(&s->vars, tq);
829                         free(v);
830                 }
831                 TAILQ_REMOVE(&p->val, s, tq);
832                 free(s);
833         }
834 }