]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/jail/config.c
MFC r264346 (by alc):
[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_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",                 0},
115     [KP_NAME] =                 {"name",                0},
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
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;
134         char *ep;
135         size_t varoff;
136         int did_self, jseq, pgen;
137
138         if (!strcmp(cfname, "-")) {
139                 cfname = "STDIN";
140                 yyin = stdin;
141         } else {
142                 yyin = fopen(cfname, "r");
143                 if (!yyin)
144                         err(1, "%s", cfname);
145         }
146         if (yyparse() || yynerrs)
147                 exit(1);
148
149         /* Separate the wildcard jails out from the actual jails. */
150         jseq = 0;
151         TAILQ_INIT(&wild);
152         TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
153                 j->seq = ++jseq;
154                 if (wild_jail_name(j->name))
155                         requeue(j, &wild);
156         }
157
158         TAILQ_FOREACH(j, &cfjails, tq) {
159                 /* Set aside the jail's parameters. */
160                 TAILQ_INIT(&opp);
161                 TAILQ_CONCAT(&opp, &j->params, tq);
162                 /*
163                  * The jail name implies its "name" or "jid" parameter,
164                  * though they may also be explicitly set later on.
165                  */
166                 add_param(j, NULL,
167                     strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME,
168                     j->name);
169                 /*
170                  * Collect parameters for the jail, global parameters/variables,
171                  * and any matching wildcard jails.
172                  */
173                 did_self = 0;
174                 TAILQ_FOREACH(wj, &wild, tq) {
175                         if (j->seq < wj->seq && !did_self) {
176                                 TAILQ_FOREACH(p, &opp, tq)
177                                         add_param(j, p, 0, NULL);
178                                 did_self = 1;
179                         }
180                         if (wild_jail_match(j->name, wj->name))
181                                 TAILQ_FOREACH(p, &wj->params, tq)
182                                         add_param(j, p, 0, NULL);
183                 }
184                 if (!did_self)
185                         TAILQ_FOREACH(p, &opp, tq)
186                                 add_param(j, p, 0, NULL);
187
188                 /* Resolve any variable substitutions. */
189                 pgen = 0;
190                 TAILQ_FOREACH(p, &j->params, tq) {
191                     p->gen = ++pgen;
192                 find_vars:
193                     TAILQ_FOREACH(s, &p->val, tq) {
194                         varoff = 0;
195                         while ((v = STAILQ_FIRST(&s->vars))) {
196                                 TAILQ_FOREACH(vp, &j->params, tq)
197                                         if (!strcmp(vp->name, v->name))
198                                                 break;
199                                 if (!vp) {
200                                         jail_warnx(j,
201                                             "%s: variable \"%s\" not found",
202                                             p->name, v->name);
203                                 bad_var:
204                                         j->flags |= JF_FAILED;
205                                         TAILQ_FOREACH(vp, &j->params, tq)
206                                                 if (vp->gen == pgen)
207                                                         vp->flags |= PF_BAD;
208                                         goto free_var;
209                                 }
210                                 if (vp->flags & PF_BAD)
211                                         goto bad_var;
212                                 if (vp->gen == pgen) {
213                                         jail_warnx(j, "%s: variable loop",
214                                             v->name);
215                                         goto bad_var;
216                                 }
217                                 TAILQ_FOREACH(vs, &vp->val, tq)
218                                         if (!STAILQ_EMPTY(&vs->vars)) {
219                                                 vp->gen = pgen;
220                                                 TAILQ_REMOVE(&j->params, vp,
221                                                     tq);
222                                                 TAILQ_INSERT_BEFORE(p, vp, tq);
223                                                 p = vp;
224                                                 goto find_vars;
225                                         }
226                                 vs = TAILQ_FIRST(&vp->val);
227                                 if (TAILQ_NEXT(vs, tq) != NULL &&
228                                     (s->s[0] != '\0' ||
229                                      STAILQ_NEXT(v, tq))) {
230                                         jail_warnx(j, "%s: array cannot be "
231                                             "substituted inline",
232                                             p->name);
233                                         goto bad_var;
234                                 }
235                                 s->s = erealloc(s->s, s->len + vs->len + 1);
236                                 memmove(s->s + v->pos + varoff + vs->len,
237                                     s->s + v->pos + varoff,
238                                     s->len - (v->pos + varoff) + 1);
239                                 memcpy(s->s + v->pos + varoff, vs->s, vs->len);
240                                 varoff += 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 *
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
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 (strcmp(dp->name, name)) {
366                         free(dp->name);
367                         dp->name = estrdup(name);
368                 }
369                 if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss))
370                         free_param_strings(dp);
371                 TAILQ_CONCAT(&dp->val, &nss, tq);
372                 dp->flags |= flags;
373         } else {
374                 /* Not found - add it. */
375                 np = emalloc(sizeof(struct cfparam));
376                 np->name = estrdup(name);
377                 TAILQ_INIT(&np->val);
378                 TAILQ_CONCAT(&np->val, &nss, tq);
379                 np->flags = flags;
380                 np->gen = 0;
381                 TAILQ_INSERT_TAIL(&j->params, np, tq);
382                 if (ipnum != IP__NULL)
383                         j->intparams[ipnum] = np;
384                 else
385                         for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++)
386                                 if (!(intparams[ipnum].flags & PF_CONV) &&
387                                     equalopts(name, intparams[ipnum].name)) {
388                                         j->intparams[ipnum] = np;
389                                         np->flags |= intparams[ipnum].flags;
390                                         break;
391                                 }
392         }
393 }
394
395 /*
396  * Return if a boolean parameter exists and is true.
397  */
398 int
399 bool_param(const struct cfparam *p)
400 {
401         const char *cs;
402
403         if (p == NULL)
404                 return 0;
405         cs = strrchr(p->name, '.');
406         return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^
407             (TAILQ_EMPTY(&p->val) ||
408              !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") ||
409              (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10)));
410 }
411
412 /*
413  * Set an integer if a parameter if it exists.
414  */
415 int
416 int_param(const struct cfparam *p, int *ip)
417 {
418         if (p == NULL || TAILQ_EMPTY(&p->val))
419                 return 0;
420         *ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10);
421         return 1;
422 }
423
424 /*
425  * Return the string value of a scalar parameter if it exists.
426  */
427 const char *
428 string_param(const struct cfparam *p)
429 {
430         return (p && !TAILQ_EMPTY(&p->val)
431             ? TAILQ_LAST(&p->val, cfstrings)->s : NULL);
432 }
433
434 /*
435  * Check syntax and values of internal parameters.  Set some internal
436  * parameters based on the values of others.
437  */
438 int
439 check_intparams(struct cfjail *j)
440 {
441         struct cfparam *p;
442         struct cfstring *s;
443         FILE *f;
444         const char *val;
445         char *cs, *ep, *ln;
446         size_t lnlen;
447         int error;
448 #if defined(INET) || defined(INET6)
449         struct addrinfo hints;
450         struct addrinfo *ai0, *ai;
451         const char *hostname;
452         int gicode, defif, prefix;
453 #endif
454 #ifdef INET
455         struct in_addr addr4;
456         int ip4ok;
457         char avalue4[INET_ADDRSTRLEN];
458 #endif
459 #ifdef INET6
460         struct in6_addr addr6;
461         int ip6ok;
462         char avalue6[INET6_ADDRSTRLEN];
463 #endif
464
465         error = 0;
466         /* Check format of boolan and integer values. */
467         TAILQ_FOREACH(p, &j->params, tq) {
468                 if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) {
469                         val = TAILQ_LAST(&p->val, cfstrings)->s;
470                         if (p->flags & PF_BOOL) {
471                                 if (strcasecmp(val, "false") &&
472                                     strcasecmp(val, "true") &&
473                                     ((void)strtol(val, &ep, 10), *ep)) {
474                                         jail_warnx(j,
475                                             "%s: unknown boolean value \"%s\"",
476                                             p->name, val);
477                                         error = -1;
478                                 }
479                         } else {
480                                 (void)strtol(val, &ep, 10);
481                                 if (ep == val || *ep) {
482                                         jail_warnx(j,
483                                             "%s: non-integer value \"%s\"",
484                                             p->name, val);
485                                         error = -1;
486                                 }
487                         }
488                 }
489         }
490
491 #if defined(INET) || defined(INET6)
492         /*
493          * The ip_hostname parameter looks up the hostname, and adds parameters
494          * for any IP addresses it finds.
495          */
496         if (((j->flags & JF_OP_MASK) != JF_STOP ||
497             j->intparams[IP_INTERFACE] != NULL) &&
498             bool_param(j->intparams[IP_IP_HOSTNAME]) &&
499             (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) {
500                 j->intparams[IP_IP_HOSTNAME] = NULL;
501                 /*
502                  * Silently ignore unsupported address families from
503                  * DNS lookups.
504                  */
505 #ifdef INET
506                 ip4ok = feature_present("inet");
507 #endif
508 #ifdef INET6
509                 ip6ok = feature_present("inet6");
510 #endif
511                 if (
512 #if defined(INET) && defined(INET6)
513                     ip4ok || ip6ok
514 #elif defined(INET)
515                     ip4ok
516 #elif defined(INET6)
517                     ip6ok
518 #endif
519                          ) {
520                         /* Look up the hostname (or get the address) */
521                         memset(&hints, 0, sizeof(hints));
522                         hints.ai_socktype = SOCK_STREAM;
523                         hints.ai_family =
524 #if defined(INET) && defined(INET6)
525                             ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) :  PF_INET6;
526 #elif defined(INET)
527                             PF_INET;
528 #elif defined(INET6)
529                             PF_INET6;
530 #endif
531                         gicode = getaddrinfo(hostname, NULL, &hints, &ai0);
532                         if (gicode != 0) {
533                                 jail_warnx(j, "host.hostname %s: %s", hostname,
534                                     gai_strerror(gicode));
535                                 error = -1;
536                         } else {
537                                 /*
538                                  * Convert the addresses to ASCII so jailparam
539                                  * can convert them back.  Errors are not
540                                  * expected here.
541                                  */
542                                 for (ai = ai0; ai; ai = ai->ai_next)
543                                         switch (ai->ai_family) {
544 #ifdef INET
545                                         case AF_INET:
546                                                 memcpy(&addr4,
547                                                     &((struct sockaddr_in *)
548                                                     (void *)ai->ai_addr)->
549                                                     sin_addr, sizeof(addr4));
550                                                 if (inet_ntop(AF_INET,
551                                                     &addr4, avalue4,
552                                                     INET_ADDRSTRLEN) == NULL)
553                                                         err(1, "inet_ntop");
554                                                 add_param(j, NULL, KP_IP4_ADDR,
555                                                     avalue4);
556                                                 break;
557 #endif
558 #ifdef INET6
559                                         case AF_INET6:
560                                                 memcpy(&addr6,
561                                                     &((struct sockaddr_in6 *)
562                                                     (void *)ai->ai_addr)->
563                                                     sin6_addr, sizeof(addr6));
564                                                 if (inet_ntop(AF_INET6,
565                                                     &addr6, avalue6,
566                                                     INET6_ADDRSTRLEN) == NULL)
567                                                         err(1, "inet_ntop");
568                                                 add_param(j, NULL, KP_IP6_ADDR,
569                                                     avalue6);
570                                                 break;
571 #endif
572                                         }
573                                 freeaddrinfo(ai0);
574                         }
575                 }
576         }
577
578         /*
579          * IP addresses may include an interface to set that address on,
580          * a netmask/suffix for that address and options for ifconfig.
581          * These are copied to an internal command parameter and then stripped
582          * so they won't be passed on to jailparam_set.
583          */
584         defif = string_param(j->intparams[IP_INTERFACE]) != NULL;
585 #ifdef INET
586         if (j->intparams[KP_IP4_ADDR] != NULL) {
587                 TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) {
588                         cs = strchr(s->s, '|');
589                         if (cs || defif)
590                                 add_param(j, NULL, IP__IP4_IFADDR, s->s);
591                         if (cs) {
592                                 strcpy(s->s, cs + 1);
593                                 s->len -= cs + 1 - s->s;
594                         }
595                         if ((cs = strchr(s->s, '/'))) {
596                                 prefix = strtol(cs + 1, &ep, 10);
597                                 if (*ep == '.'
598                                     ? inet_pton(AF_INET, cs + 1, &addr4) != 1
599                                     : *ep || prefix < 0 || prefix > 32) {
600                                         jail_warnx(j,
601                                             "ip4.addr: bad netmask \"%s\"", cs);
602                                         error = -1;     
603                                 }
604                                 *cs = '\0';
605                                 s->len = cs - s->s;
606                         }
607                         if ((cs = strchr(s->s, ' ')) != NULL) {
608                                 *cs = '\0';
609                                 s->len = cs - s->s;
610                         }
611                 }
612         }
613 #endif
614 #ifdef INET6
615         if (j->intparams[KP_IP6_ADDR] != NULL) {
616                 TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) {
617                         cs = strchr(s->s, '|');
618                         if (cs || defif)
619                                 add_param(j, NULL, IP__IP6_IFADDR, s->s);
620                         if (cs) {
621                                 strcpy(s->s, cs + 1);
622                                 s->len -= cs + 1 - s->s;
623                         }
624                         if ((cs = strchr(s->s, '/'))) {
625                                 prefix = strtol(cs + 1, &ep, 10);
626                                 if (*ep || prefix < 0 || prefix > 128) {
627                                         jail_warnx(j,
628                                             "ip6.addr: bad prefixlen \"%s\"",
629                                             cs);
630                                         error = -1;     
631                                 }
632                                 *cs = '\0';
633                                 s->len = cs - s->s;
634                         }
635                         if ((cs = strchr(s->s, ' ')) != NULL) {
636                                 *cs = '\0';
637                                 s->len = cs - s->s;
638                         }
639                 }
640         }
641 #endif
642 #endif
643
644         /*
645          * Read mount.fstab file(s), and treat each line as its own mount
646          * parameter.
647          */
648         if (j->intparams[IP_MOUNT_FSTAB] != NULL) {
649                 TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) {
650                         if (s->len == 0)
651                                 continue;
652                         f = fopen(s->s, "r");
653                         if (f == NULL) {
654                                 jail_warnx(j, "mount.fstab: %s: %s",
655                                     s->s, strerror(errno));
656                                 error = -1;
657                                 continue;
658                         }
659                         while ((ln = fgetln(f, &lnlen))) {
660                                 if ((cs = memchr(ln, '#', lnlen - 1)))
661                                         lnlen = cs - ln + 1;
662                                 if (ln[lnlen - 1] == '\n' ||
663                                     ln[lnlen - 1] == '#')
664                                         ln[lnlen - 1] = '\0';
665                                 else {
666                                         cs = alloca(lnlen + 1);
667                                         strlcpy(cs, ln, lnlen + 1);
668                                         ln = cs;
669                                 }
670                                 add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln);
671                         }
672                         fclose(f);
673                 }
674         }
675         if (error)
676                 failed(j);
677         return error;
678 }
679
680 /*
681  * Import parameters into libjail's binary jailparam format.
682  */
683 int
684 import_params(struct cfjail *j)
685 {
686         struct cfparam *p;
687         struct cfstring *s, *ts;
688         struct jailparam *jp;
689         char *value, *cs;
690         size_t vallen;
691         int error;
692
693         error = 0;
694         j->njp = 0;
695         TAILQ_FOREACH(p, &j->params, tq)
696                 if (!(p->flags & PF_INTERNAL))
697                         j->njp++;
698         j->jp = jp = emalloc(j->njp * sizeof(struct jailparam));
699         TAILQ_FOREACH(p, &j->params, tq) {
700                 if (p->flags & PF_INTERNAL)
701                         continue;
702                 if (jailparam_init(jp, p->name) < 0) {
703                         error = -1;
704                         jail_warnx(j, "%s", jail_errmsg);
705                         jp++;
706                         continue;
707                 }
708                 if (TAILQ_EMPTY(&p->val))
709                         value = NULL;
710                 else if (!jp->jp_elemlen ||
711                          !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) {
712                         /*
713                          * Scalar parameters silently discard multiple (array)
714                          * values, keeping only the last value added.  This
715                          * lets values added from the command line append to
716                          * arrays wthout pre-checking the type.
717                          */
718                         value = TAILQ_LAST(&p->val, cfstrings)->s;
719                 } else {
720                         /*
721                          * Convert arrays into comma-separated strings, which
722                          * jailparam_import will then convert back into arrays.
723                          */
724                         vallen = 0;
725                         TAILQ_FOREACH(s, &p->val, tq)
726                                 vallen += s->len + 1;
727                         value = alloca(vallen);
728                         cs = value;
729                         TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
730                                 memcpy(cs, s->s, s->len);
731                                 cs += s->len + 1;
732                                 cs[-1] = ',';
733                         }
734                         value[vallen - 1] = '\0';
735                 }
736                 if (jailparam_import(jp, value) < 0) {
737                         error = -1;
738                         jail_warnx(j, "%s", jail_errmsg);
739                 }
740                 jp++;
741         }
742         if (error) {
743                 jailparam_free(j->jp, j->njp);
744                 free(j->jp);
745                 j->jp = NULL;
746                 failed(j);
747         }
748         return error;
749 }
750
751 /*
752  * Check if options are equal (with or without the "no" prefix).
753  */
754 int
755 equalopts(const char *opt1, const char *opt2)
756 {
757         char *p;
758
759         /* "opt" vs. "opt" or "noopt" vs. "noopt" */
760         if (strcmp(opt1, opt2) == 0)
761                 return (1);
762         /* "noopt" vs. "opt" */
763         if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
764                 return (1);
765         /* "opt" vs. "noopt" */
766         if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
767                 return (1);
768         while ((p = strchr(opt1, '.')) != NULL &&
769             !strncmp(opt1, opt2, ++p - opt1)) {
770                 opt2 += p - opt1;
771                 opt1 = p;
772                 /* "foo.noopt" vs. "foo.opt" */
773                 if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
774                         return (1);
775                 /* "foo.opt" vs. "foo.noopt" */
776                 if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
777                         return (1);
778         }
779         return (0);
780 }
781
782 /*
783  * See if a jail name matches a wildcard.
784  */
785 int
786 wild_jail_match(const char *jname, const char *wname)
787 {
788         const char *jc, *jd, *wc, *wd;
789
790         /*
791          * A non-final "*" component in the wild name matches a single jail
792          * component, and a final "*" matches one or more jail components.
793          */
794         for (jc = jname, wc = wname;
795              (jd = strchr(jc, '.')) && (wd = strchr(wc, '.'));
796              jc = jd + 1, wc = wd + 1)
797                 if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2))
798                         return 0;
799         return (!strcmp(jc, wc) || !strcmp(wc, "*"));
800 }
801
802 /*
803  * Return if a jail name is a wildcard.
804  */
805 int
806 wild_jail_name(const char *wname)
807 {
808         const char *wc;
809
810         for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*'))
811                 if ((wc == wname || wc[-1] == '.') &&
812                     (wc[1] == '\0' || wc[1] == '.'))
813                         return 1;
814         return 0;
815 }
816
817 /*
818  * Free a parameter record and all its strings and variables.
819  */
820 static void
821 free_param(struct cfparams *pp, struct cfparam *p)
822 {
823         free(p->name);
824         free_param_strings(p);
825         TAILQ_REMOVE(pp, p, tq);
826         free(p);
827 }
828
829 static void
830 free_param_strings(struct cfparam *p)
831 {
832         struct cfstring *s;
833         struct cfvar *v;
834
835         while ((s = TAILQ_FIRST(&p->val))) {
836                 free(s->s);
837                 while ((v = STAILQ_FIRST(&s->vars))) {
838                         free(v->name);
839                         STAILQ_REMOVE_HEAD(&s->vars, tq);
840                         free(v);
841                 }
842                 TAILQ_REMOVE(&p->val, s, tq);
843                 free(s);
844         }
845 }