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