]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/jail/config.c
Merge r268488 from head:
[FreeBSD/stable/10.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          * a netmask/suffix for that address and options for ifconfig.
580          * These are copied to an internal command parameter and then stripped
581          * so they won't be passed on to jailparam_set.
582          */
583         defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
584 #ifdef INET
585         if (j->intparams[KP_IP4_ADDR] != NULL) {
586                 TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
587                         cs = strchr(s->s, '|');
588                         if (cs || defif)
589                                 add_param(j, NULL, IP__IP4_IFADDR, s->s);
590                         if (cs) {
591                                 strcpy(s->s, cs + 1);
592                                 s->len -= cs + 1 - s->s;
593                         }
594                         if ((cs = strchr(s->s, '/'))) {
595                                 prefix = strtol(cs + 1, &ep, 10);
596                                 if (*ep == '.'
597                                     ? inet_pton(AF_INET, cs + 1, &addr4) != 1
598                                     : *ep || prefix < 0 || prefix > 32) {
599                                         jail_warnx(j,
600                                             "ip4.addr: bad netmask \"%s\"", cs);
601                                         error = -1;     
602                                 }
603                                 *cs = '\0';
604                                 s->len = cs - s->s;
605                         }
606                         if ((cs = strchr(s->s, ' ')) != NULL) {
607                                 *cs = '\0';
608                                 s->len = cs - s->s;
609                         }
610                 }
611         }
612 #endif
613 #ifdef INET6
614         if (j->intparams[KP_IP6_ADDR] != NULL) {
615                 TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
616                         cs = strchr(s->s, '|');
617                         if (cs || defif)
618                                 add_param(j, NULL, IP__IP6_IFADDR, s->s);
619                         if (cs) {
620                                 strcpy(s->s, cs + 1);
621                                 s->len -= cs + 1 - s->s;
622                         }
623                         if ((cs = strchr(s->s, '/'))) {
624                                 prefix = strtol(cs + 1, &ep, 10);
625                                 if (*ep || prefix < 0 || prefix > 128) {
626                                         jail_warnx(j,
627                                             "ip6.addr: bad prefixlen \"%s\"",
628                                             cs);
629                                         error = -1;     
630                                 }
631                                 *cs = '\0';
632                                 s->len = cs - s->s;
633                         }
634                         if ((cs = strchr(s->s, ' ')) != NULL) {
635                                 *cs = '\0';
636                                 s->len = cs - s->s;
637                         }
638                 }
639         }
640 #endif
641 #endif
642
643         /*
644          * Read mount.fstab file(s), and treat each line as its own mount
645          * parameter.
646          */
647         if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
648                 TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
649                         if (s->len == 0)
650                                 continue;
651                         f = fopen(s->s, "r");
652                         if (f == NULL) {
653                                 jail_warnx(j, "mount.fstab: %s: %s",
654                                     s->s, strerror(errno));
655                                 error = -1;
656                                 continue;
657                         }
658                         while ((ln = fgetln(f, &lnlen))) {
659                                 if ((cs = memchr(ln, '#', lnlen - 1)))
660                                         lnlen = cs - ln + 1;
661                                 if (ln[lnlen - 1] == '\n' ||
662                                     ln[lnlen - 1] == '#')
663                                         ln[lnlen - 1] = '\0';
664                                 else {
665                                         cs = alloca(lnlen + 1);
666                                         strlcpy(cs, ln, lnlen + 1);
667                                         ln = cs;
668                                 }
669                                 add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
670                         }
671                         fclose(f);
672                 }
673         }
674         if (error)
675                 failed(j);
676         return error;
677 }
678
679 /*
680  * Import parameters into libjail's binary jailparam format.
681  */
682 int
683 import_params(struct cfjail *j)
684 {
685         struct cfparam *p;
686         struct cfstring *s, *ts;
687         struct jailparam *jp;
688         char *value, *cs;
689         size_t vallen;
690         int error;
691
692         error = 0;
693         j->njp = 0;
694         TAILQ_FOREACH(p, &j->params, tq)
695                 if (!(p->flags & PF_INTERNAL))
696                         j->njp++;
697         j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
698         TAILQ_FOREACH(p, &j->params, tq) {
699                 if (p->flags & PF_INTERNAL)
700                         continue;
701                 if (jailparam_init(jp, p->name) < 0) {
702                         error = -1;
703                         jail_warnx(j, "%s", jail_errmsg);
704                         jp++;
705                         continue;
706                 }
707                 if (TAILQ_EMPTY(&p->val))
708                         value = NULL;
709                 else if (!jp->jp_elemlen ||
710                          !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
711                         /*
712                          * Scalar parameters silently discard multiple (array)
713                          * values, keeping only the last value added.  This
714                          * lets values added from the command line append to
715                          * arrays wthout pre-checking the type.
716                          */
717                         value = TAILQ_LAST(&p->val, cfstrings)->s;
718                 } else {
719                         /*
720                          * Convert arrays into comma-separated strings, which
721                          * jailparam_import will then convert back into arrays.
722                          */
723                         vallen = 0;
724                         TAILQ_FOREACH(s, &p->val, tq)
725                                 vallen += s->len + 1;
726                         value = alloca(vallen);
727                         cs = value;
728                         TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
729                                 memcpy(cs, s->s, s->len);
730                                 cs += s->len + 1;
731                                 cs[-1] = ',';
732                         }
733                         value[vallen - 1] = '\0';
734                 }
735                 if (jailparam_import(jp, value) < 0) {
736                         error = -1;
737                         jail_warnx(j, "%s", jail_errmsg);
738                 }
739                 jp++;
740         }
741         if (error) {
742                 jailparam_free(j->jp, j->njp);
743                 free(j->jp);
744                 j->jp = NULL;
745                 failed(j);
746         }
747         return error;
748 }
749
750 /*
751  * Check if options are equal (with or without the "no" prefix).
752  */
753 int
754 equalopts(const char *opt1, const char *opt2)
755 {
756         char *p;
757
758         /* "opt" vs. "opt" or "noopt" vs. "noopt" */
759         if (strcmp(opt1, opt2) == 0)
760                 return (1);
761         /* "noopt" vs. "opt" */
762         if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
763                 return (1);
764         /* "opt" vs. "noopt" */
765         if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
766                 return (1);
767         while ((p = strchr(opt1, '.')) != NULL &&
768             !strncmp(opt1, opt2, ++p - opt1)) {
769                 opt2 += p - opt1;
770                 opt1 = p;
771                 /* "foo.noopt" vs. "foo.opt" */
772                 if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
773                         return (1);
774                 /* "foo.opt" vs. "foo.noopt" */
775                 if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
776                         return (1);
777         }
778         return (0);
779 }
780
781 /*
782  * See if a jail name matches a wildcard.
783  */
784 int
785 wild_jail_match(const char *jname, const char *wname)
786 {
787         const char *jc, *jd, *wc, *wd;
788
789         /*
790          * A non-final "*" component in the wild name matches a single jail
791          * component, and a final "*" matches one or more jail components.
792          */
793         for (jc = jname, wc = wname;
794              (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
795              jc = jd + 1, wc = wd + 1)
796                 if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
797                         return 0;
798         return (!strcmp(jc, wc) || !strcmp(wc, "*"));
799 }
800
801 /*
802  * Return if a jail name is a wildcard.
803  */
804 int
805 wild_jail_name(const char *wname)
806 {
807         const char *wc;
808
809         for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
810                 if ((wc == wname || wc[-1] == '.') &&
811                     (wc[1] == '\0' || wc[1] == '.'))
812                         return 1;
813         return 0;
814 }
815
816 /*
817  * Free a parameter record and all its strings and variables.
818  */
819 static void
820 free_param(struct cfparams *pp, struct cfparam *p)
821 {
822         free(p->name);
823         free_param_strings(p);
824         TAILQ_REMOVE(pp, p, tq);
825         free(p);
826 }
827
828 static void
829 free_param_strings(struct cfparam *p)
830 {
831         struct cfstring *s;
832         struct cfvar *v;
833
834         while ((s = TAILQ_FIRST(&p->val))) {
835                 free(s->s);
836                 while ((v = STAILQ_FIRST(&s->vars))) {
837                         free(v->name);
838                         STAILQ_REMOVE_HEAD(&s->vars, tq);
839                         free(v);
840                 }
841                 TAILQ_REMOVE(&p->val, s, tq);
842                 free(s);
843         }
844 }