1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 1999 Poul-Henning Kamp. 5 * Copyright (c) 2009-2012 James Gritton 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/stat.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 <errno.h> 40 #include <stdarg.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "jailp.h" 47 48 #define JP_RDTUN(jp) (((jp)->jp_ctltype & CTLFLAG_RDTUN) == CTLFLAG_RDTUN) 49 50 struct permspec { 51 const char *name; 52 enum intparam ipnum; 53 int rev; 54 }; 55 56 int iflag; 57 int note_remove; 58 int verbose; 59 const char *separator = "\t"; 60 61 static void clear_persist(struct cfjail *j); 62 static int update_jail(struct cfjail *j); 63 static int rdtun_params(struct cfjail *j, int dofail); 64 static void running_jid(struct cfjail *j); 65 static void jail_quoted_warnx(const struct cfjail *j, const char *name_msg, 66 const char *noname_msg); 67 static int jailparam_set_note(const struct cfjail *j, struct jailparam *jp, 68 unsigned njp, int flags); 69 static void print_jail(FILE *fp, struct cfjail *j, int oldcl, int running); 70 static void print_param(FILE *fp, const struct cfparam *p, int sep, int doname); 71 static void show_jails(void); 72 static void quoted_print(FILE *fp, char *str); 73 static void usage(void) __dead2; 74 75 static struct permspec perm_sysctl[] = { 76 { "security.jail.set_hostname_allowed", KP_ALLOW_SET_HOSTNAME, 0 }, 77 { "security.jail.sysvipc_allowed", KP_ALLOW_SYSVIPC, 0 }, 78 { "security.jail.allow_raw_sockets", KP_ALLOW_RAW_SOCKETS, 0 }, 79 { "security.jail.chflags_allowed", KP_ALLOW_CHFLAGS, 0 }, 80 { "security.jail.mount_allowed", KP_ALLOW_MOUNT, 0 }, 81 { "security.jail.socket_unixiproute_only", KP_ALLOW_SOCKET_AF, 1 }, 82 }; 83 84 static const enum intparam startcommands[] = { 85 IP__NULL, 86 IP_EXEC_PREPARE, 87 #ifdef INET 88 IP__IP4_IFADDR, 89 #endif 90 #ifdef INET6 91 IP__IP6_IFADDR, 92 #endif 93 IP_MOUNT, 94 IP__MOUNT_FROM_FSTAB, 95 IP_MOUNT_DEVFS, 96 IP_MOUNT_FDESCFS, 97 IP_MOUNT_PROCFS, 98 IP_EXEC_PRESTART, 99 IP__OP, 100 IP_EXEC_CREATED, 101 IP_ZFS_DATASET, 102 IP_VNET_INTERFACE, 103 IP_EXEC_START, 104 IP_COMMAND, 105 IP_EXEC_POSTSTART, 106 IP__NULL 107 }; 108 109 static const enum intparam stopcommands[] = { 110 IP__NULL, 111 IP_EXEC_PRESTOP, 112 IP_EXEC_STOP, 113 IP_STOP_TIMEOUT, 114 IP__OP, 115 IP_EXEC_POSTSTOP, 116 IP_MOUNT_PROCFS, 117 IP_MOUNT_FDESCFS, 118 IP_MOUNT_DEVFS, 119 IP__MOUNT_FROM_FSTAB, 120 IP_MOUNT, 121 #ifdef INET6 122 IP__IP6_IFADDR, 123 #endif 124 #ifdef INET 125 IP__IP4_IFADDR, 126 #endif 127 IP_EXEC_RELEASE, 128 IP__NULL 129 }; 130 131 static const enum intparam cleancommands[] = { 132 IP__NULL, 133 IP_EXEC_POSTSTOP, 134 IP_MOUNT_PROCFS, 135 IP_MOUNT_FDESCFS, 136 IP_MOUNT_DEVFS, 137 IP__MOUNT_FROM_FSTAB, 138 IP_MOUNT, 139 #ifdef INET6 140 IP__IP6_IFADDR, 141 #endif 142 #ifdef INET 143 IP__IP4_IFADDR, 144 #endif 145 IP_EXEC_RELEASE, 146 IP__NULL 147 }; 148 149 int 150 main(int argc, char **argv) 151 { 152 struct stat st; 153 FILE *jfp; 154 struct cfjail *j; 155 char *JidFile; 156 const char *cfname; 157 size_t sysvallen; 158 unsigned op, pi; 159 int ch, docf, dying_warned, error, i, oldcl, sysval; 160 int dflag, eflag, Rflag; 161 #if defined(INET) || defined(INET6) 162 char *cs, *ncs; 163 #endif 164 #if defined(INET) && defined(INET6) 165 struct in6_addr addr6; 166 #endif 167 168 op = 0; 169 dflag = eflag = Rflag = 0; 170 docf = 1; 171 cfname = CONF_FILE; 172 JidFile = NULL; 173 174 while ((ch = getopt(argc, argv, "cCde:f:hiJ:lmn:p:qrRs:u:U:v")) != -1) { 175 switch (ch) { 176 case 'c': 177 op |= JF_START; 178 break; 179 case 'C': 180 op |= JF_CLEANUP; 181 break; 182 case 'd': 183 dflag = 1; 184 break; 185 case 'e': 186 eflag = 1; 187 separator = optarg; 188 break; 189 case 'f': 190 cfname = optarg; 191 break; 192 case 'h': 193 #if defined(INET) || defined(INET6) 194 add_param(NULL, NULL, IP_IP_HOSTNAME, NULL); 195 #endif 196 docf = 0; 197 break; 198 case 'i': 199 iflag = 1; 200 verbose = -1; 201 break; 202 case 'J': 203 JidFile = optarg; 204 break; 205 case 'l': 206 add_param(NULL, NULL, IP_EXEC_CLEAN, NULL); 207 docf = 0; 208 break; 209 case 'm': 210 op |= JF_SET; 211 break; 212 case 'n': 213 add_param(NULL, NULL, KP_NAME, optarg); 214 docf = 0; 215 break; 216 case 'p': 217 paralimit = strtol(optarg, NULL, 10); 218 if (paralimit == 0) 219 paralimit = -1; 220 break; 221 case 'q': 222 verbose = -1; 223 break; 224 case 'r': 225 op |= JF_STOP; 226 break; 227 case 'R': 228 op |= JF_STOP; 229 Rflag = 1; 230 break; 231 case 's': 232 add_param(NULL, NULL, KP_SECURELEVEL, optarg); 233 docf = 0; 234 break; 235 case 'u': 236 add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg); 237 add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER, NULL); 238 docf = 0; 239 break; 240 case 'U': 241 add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg); 242 add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER, 243 "false"); 244 docf = 0; 245 break; 246 case 'v': 247 verbose = 1; 248 break; 249 default: 250 usage(); 251 } 252 } 253 argc -= optind; 254 argv += optind; 255 256 if (eflag) { 257 /* Just print list of all configured non-wildcard jails */ 258 if (op || argc > 0) 259 usage(); 260 load_config(cfname); 261 show_jails(); 262 exit(0); 263 } 264 265 /* Find out which of the command line styles this is. */ 266 oldcl = 0; 267 if (!op) { 268 /* Old-style command line with four fixed parameters */ 269 if (argc < 4 || argv[0][0] != '/') 270 usage(); 271 op = JF_START; 272 docf = 0; 273 oldcl = 1; 274 add_param(NULL, NULL, KP_PATH, argv[0]); 275 add_param(NULL, NULL, KP_HOST_HOSTNAME, argv[1]); 276 #if defined(INET) || defined(INET6) 277 if (argv[2][0] != '\0') { 278 for (cs = argv[2];; cs = ncs + 1) { 279 ncs = strchr(cs, ','); 280 if (ncs) 281 *ncs = '\0'; 282 add_param(NULL, NULL, 283 #if defined(INET) && defined(INET6) 284 inet_pton(AF_INET6, cs, &addr6) == 1 285 ? KP_IP6_ADDR : KP_IP4_ADDR, 286 #elif defined(INET) 287 KP_IP4_ADDR, 288 #elif defined(INET6) 289 KP_IP6_ADDR, 290 #endif 291 cs); 292 if (!ncs) 293 break; 294 } 295 } 296 #endif 297 for (i = 3; i < argc; i++) 298 add_param(NULL, NULL, IP_COMMAND, argv[i]); 299 /* Emulate the defaults from security.jail.* sysctls. */ 300 sysvallen = sizeof(sysval); 301 if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen, 302 NULL, 0) == 0 && sysval == 0) { 303 for (pi = 0; pi < sizeof(perm_sysctl) / 304 sizeof(perm_sysctl[0]); pi++) { 305 sysvallen = sizeof(sysval); 306 if (sysctlbyname(perm_sysctl[pi].name, 307 &sysval, &sysvallen, NULL, 0) == 0) 308 add_param(NULL, NULL, 309 perm_sysctl[pi].ipnum, 310 (sysval ? 1 : 0) ^ 311 perm_sysctl[pi].rev 312 ? NULL : "false"); 313 } 314 } 315 } else if (op == JF_STOP) { 316 /* Jail remove, perhaps using the config file */ 317 if (!docf || argc == 0) 318 usage(); 319 if (!Rflag) 320 for (i = 0; i < argc; i++) 321 if (strchr(argv[i], '=')) 322 usage(); 323 if ((docf = !Rflag && 324 (!strcmp(cfname, "-") || stat(cfname, &st) == 0))) 325 load_config(cfname); 326 note_remove = docf || argc > 1 || wild_jail_name(argv[0]); 327 } else if (argc > 1 || (argc == 1 && strchr(argv[0], '='))) { 328 /* Single jail specified on the command line */ 329 if (Rflag || (op & JF_CLEANUP)) 330 usage(); 331 docf = 0; 332 for (i = 0; i < argc; i++) { 333 if (!strncmp(argv[i], "command", 7) && 334 (argv[i][7] == '\0' || argv[i][7] == '=')) { 335 if (argv[i][7] == '=') 336 add_param(NULL, NULL, IP_COMMAND, 337 argv[i] + 8); 338 for (i++; i < argc; i++) 339 add_param(NULL, NULL, IP_COMMAND, 340 argv[i]); 341 } 342 #ifdef INET 343 else if (!strncmp(argv[i], "ip4.addr=", 9)) { 344 for (cs = argv[i] + 9;; cs = ncs + 1) { 345 ncs = strchr(cs, ','); 346 if (ncs) 347 *ncs = '\0'; 348 add_param(NULL, NULL, KP_IP4_ADDR, cs); 349 if (!ncs) 350 break; 351 } 352 } 353 #endif 354 #ifdef INET6 355 else if (!strncmp(argv[i], "ip6.addr=", 9)) { 356 for (cs = argv[i] + 9;; cs = ncs + 1) { 357 ncs = strchr(cs, ','); 358 if (ncs) 359 *ncs = '\0'; 360 add_param(NULL, NULL, KP_IP6_ADDR, cs); 361 if (!ncs) 362 break; 363 } 364 } 365 #endif 366 else 367 add_param(NULL, NULL, 0, argv[i]); 368 } 369 } else { 370 /* From the config file, perhaps with a specified jail */ 371 if (Rflag || !docf) 372 usage(); 373 load_config(cfname); 374 } 375 376 /* Find out which jails will be run. */ 377 dep_setup(docf); 378 error = 0; 379 if ((op & JF_OP_MASK) == JF_STOP) { 380 for (i = 0; i < argc; i++) 381 if (start_state(argv[i], docf, op, Rflag) < 0) 382 error = 1; 383 } else { 384 if (start_state(argv[0], docf, op, 0) < 0) 385 exit(1); 386 } 387 388 jfp = NULL; 389 if (JidFile != NULL) { 390 jfp = fopen(JidFile, "w"); 391 if (jfp == NULL) 392 err(1, "open %s", JidFile); 393 setlinebuf(jfp); 394 } 395 setlinebuf(stdout); 396 397 /* 398 * The main loop: Get an available jail and perform the required 399 * operation on it. When that is done, the jail may be finished, 400 * or it may go back for the next step. 401 */ 402 dying_warned = 0; 403 while ((j = next_jail())) 404 { 405 if (j->flags & JF_FAILED) { 406 error = 1; 407 if (j->comparam == NULL) { 408 dep_done(j, 0); 409 continue; 410 } 411 } 412 if (!(j->flags & JF_PARAMS)) 413 { 414 j->flags |= JF_PARAMS; 415 if (dflag) 416 add_param(j, NULL, IP_ALLOW_DYING, NULL); 417 if (check_intparams(j) < 0) 418 continue; 419 if ((j->flags & (JF_START | JF_SET)) && 420 import_params(j) < 0) 421 continue; 422 } 423 if (j->intparams[IP_ALLOW_DYING] && !dying_warned) { 424 warnx("%s", "the 'allow.dying' parameter and '-d' flag " 425 "are deprecated and have no effect."); 426 dying_warned = 1; 427 } 428 if (!j->jid) 429 running_jid(j); 430 if (finish_command(j)) 431 continue; 432 433 switch (j->flags & JF_OP_MASK) { 434 /* 435 * These operations just turn into a different op 436 * depending on the jail's current status. 437 */ 438 case JF_START_SET: 439 j->flags = j->jid < 0 440 ? (j->flags & JF_CLEANUP) | JF_START : JF_SET; 441 break; 442 case JF_SET_RESTART: 443 if (j->jid < 0 && !(j->flags & JF_CLEANUP)) { 444 jail_quoted_warnx(j, "not found", 445 "no jail specified"); 446 failed(j); 447 continue; 448 } 449 j->flags = rdtun_params(j, 0) 450 ? (j->flags & JF_CLEANUP) | JF_RESTART : JF_SET; 451 if (j->flags == JF_RESTART) 452 dep_reset(j); 453 break; 454 case JF_START_SET_RESTART: 455 j->flags = j->jid < 0 ? JF_START : rdtun_params(j, 0) 456 ? (j->flags & JF_CLEANUP) | JF_RESTART : JF_SET; 457 if (j->flags == JF_RESTART) 458 dep_reset(j); 459 } 460 461 switch (j->flags & JF_OP_MASK) { 462 case JF_START: 463 if (j->comparam == NULL) { 464 if (j->jid > 0 && 465 !(j->flags & (JF_DEPEND | JF_WILD))) { 466 jail_quoted_warnx(j, "already exists", 467 NULL); 468 failed(j); 469 continue; 470 } 471 if (dep_check(j)) 472 continue; 473 if (j->jid > 0) 474 goto jail_create_done; 475 if (j->flags & JF_CLEANUP) { 476 j->flags |= JF_STOP; 477 j->comparam = cleancommands; 478 } else 479 j->comparam = startcommands; 480 j->comparam = startcommands; 481 j->comstring = NULL; 482 } 483 if (next_command(j)) 484 continue; 485 if (j->flags & JF_STOP) 486 goto jail_remove_done; 487 jail_create_done: 488 clear_persist(j); 489 if (jfp != NULL) 490 print_jail(jfp, j, oldcl, 1); 491 dep_done(j, 0); 492 break; 493 494 case JF_SET: 495 if (j->jid < 0 && !(j->flags & JF_DEPEND)) { 496 jail_quoted_warnx(j, "not found", 497 "no jail specified"); 498 failed(j); 499 continue; 500 } 501 if (dep_check(j)) 502 continue; 503 if (!(j->flags & JF_DEPEND)) { 504 if (rdtun_params(j, 1) < 0 || 505 update_jail(j) < 0) 506 continue; 507 if (verbose >= 0 && (j->name || verbose > 0)) 508 jail_note(j, "updated\n"); 509 } 510 dep_done(j, 0); 511 break; 512 513 case JF_STOP: 514 case JF_RESTART: 515 if (j->comparam == NULL) { 516 if (dep_check(j)) 517 continue; 518 if (j->flags & JF_CLEANUP) { 519 j->comparam = j->jid < 0 520 ? cleancommands : stopcommands; 521 } else if (j->jid < 0) { 522 if (!(j->flags & (JF_DEPEND|JF_WILD))) { 523 if (verbose >= 0) 524 jail_quoted_warnx(j, 525 "not found", NULL); 526 failed(j); 527 } 528 goto jail_remove_done; 529 } 530 else 531 j->comparam = stopcommands; 532 j->comstring = NULL; 533 } else if ((j->flags & JF_FAILED) && j->jid > 0) 534 goto jail_remove_done; 535 if (next_command(j)) 536 continue; 537 jail_remove_done: 538 dep_done(j, 0); 539 if ((j->flags & (JF_START | JF_FAILED)) == JF_START) { 540 j->comparam = NULL; 541 j->flags &= ~(JF_STOP | JF_CLEANUP); 542 dep_reset(j); 543 requeue(j, j->ndeps ? &depend : &ready); 544 } 545 break; 546 } 547 } 548 549 if (jfp != NULL) 550 fclose(jfp); 551 exit(error); 552 } 553 554 /* 555 * Mark a jail's failure for future handling. 556 */ 557 void 558 failed(struct cfjail *j) 559 { 560 j->flags |= JF_FAILED; 561 TAILQ_REMOVE(j->queue, j, tq); 562 TAILQ_INSERT_HEAD(&ready, j, tq); 563 j->queue = &ready; 564 } 565 566 /* 567 * Exit slightly more gracefully when out of memory. 568 */ 569 void * 570 emalloc(size_t size) 571 { 572 void *p; 573 574 p = malloc(size); 575 if (!p) 576 err(1, "malloc"); 577 return p; 578 } 579 580 void * 581 erealloc(void *ptr, size_t size) 582 { 583 void *p; 584 585 p = realloc(ptr, size); 586 if (!p) 587 err(1, "malloc"); 588 return p; 589 } 590 591 char * 592 estrdup(const char *str) 593 { 594 char *ns; 595 596 ns = strdup(str); 597 if (!ns) 598 err(1, "malloc"); 599 return ns; 600 } 601 602 /* 603 * Print a message including an optional jail name. 604 */ 605 void 606 jail_note(const struct cfjail *j, const char *fmt, ...) 607 { 608 va_list ap, tap; 609 char *cs; 610 size_t len; 611 612 va_start(ap, fmt); 613 va_copy(tap, ap); 614 len = vsnprintf(NULL, 0, fmt, tap); 615 va_end(tap); 616 cs = alloca(len + 1); 617 (void)vsnprintf(cs, len + 1, fmt, ap); 618 va_end(ap); 619 if (j->name) 620 printf("%s: %s", j->name, cs); 621 else 622 printf("%s", cs); 623 } 624 625 /* 626 * Print a warning message including an optional jail name. 627 */ 628 void 629 jail_warnx(const struct cfjail *j, const char *fmt, ...) 630 { 631 va_list ap, tap; 632 char *cs; 633 size_t len; 634 635 va_start(ap, fmt); 636 va_copy(tap, ap); 637 len = vsnprintf(NULL, 0, fmt, tap); 638 va_end(tap); 639 cs = alloca(len + 1); 640 (void)vsnprintf(cs, len + 1, fmt, ap); 641 va_end(ap); 642 if (j->name) 643 warnx("%s: %s", j->name, cs); 644 else 645 warnx("%s", cs); 646 } 647 648 /* 649 * Create a new jail. 650 */ 651 int 652 create_jail(struct cfjail *j) 653 { 654 struct stat st; 655 struct jailparam *jp, *setparams, *sjp; 656 const char *path; 657 int dopersist, ns; 658 659 /* 660 * Check the jail's path, with a better error message than jail_set 661 * gives. 662 */ 663 if ((path = string_param(j->intparams[KP_PATH]))) { 664 if (j->name != NULL && path[0] != '/') { 665 jail_warnx(j, "path %s: not an absolute pathname", 666 path); 667 return -1; 668 } 669 if (stat(path, &st) < 0) { 670 jail_warnx(j, "path %s: %s", path, strerror(errno)); 671 return -1; 672 } 673 if (!S_ISDIR(st.st_mode)) { 674 jail_warnx(j, "path %s: %s", path, strerror(ENOTDIR)); 675 return -1; 676 } 677 } 678 679 /* 680 * Copy all the parameters, except that "persist" is always set when 681 * there are commands to run later. 682 */ 683 dopersist = !bool_param(j->intparams[KP_PERSIST]) && 684 (j->intparams[IP_EXEC_START] || j->intparams[IP_COMMAND] || 685 j->intparams[IP_EXEC_POSTSTART]); 686 sjp = setparams = 687 alloca((j->njp + dopersist) * sizeof(struct jailparam)); 688 if (dopersist && jailparam_init(sjp++, "persist") < 0) { 689 jail_warnx(j, "%s", jail_errmsg); 690 return -1; 691 } 692 for (jp = j->jp; jp < j->jp + j->njp; jp++) 693 if (!dopersist || !equalopts(jp->jp_name, "persist")) 694 *sjp++ = *jp; 695 ns = sjp - setparams; 696 697 j->jid = jailparam_set_note(j, setparams, ns, JAIL_CREATE); 698 if (j->jid < 0) { 699 jail_warnx(j, "%s", jail_errmsg); 700 failed(j); 701 } 702 if (dopersist) { 703 jailparam_free(setparams, 1); 704 if (j->jid > 0) 705 j->flags |= JF_PERSIST; 706 } 707 return j->jid; 708 } 709 710 /* 711 * Remove a temporarily set "persist" parameter. 712 */ 713 static void 714 clear_persist(struct cfjail *j) 715 { 716 struct iovec jiov[4]; 717 int jid; 718 719 if (!(j->flags & JF_PERSIST)) 720 return; 721 j->flags &= ~JF_PERSIST; 722 jiov[0].iov_base = __DECONST(char *, "jid"); 723 jiov[0].iov_len = sizeof("jid"); 724 jiov[1].iov_base = &j->jid; 725 jiov[1].iov_len = sizeof(j->jid); 726 jiov[2].iov_base = __DECONST(char *, "nopersist"); 727 jiov[2].iov_len = sizeof("nopersist"); 728 jiov[3].iov_base = NULL; 729 jiov[3].iov_len = 0; 730 jid = jail_set(jiov, 4, JAIL_UPDATE); 731 if (verbose > 0) 732 jail_note(j, "jail_set(JAIL_UPDATE) jid=%d nopersist%s%s\n", 733 j->jid, jid < 0 ? ": " : "", 734 jid < 0 ? strerror(errno) : ""); 735 } 736 737 /* 738 * Set a jail's parameters. 739 */ 740 static int 741 update_jail(struct cfjail *j) 742 { 743 struct jailparam *jp, *setparams, *sjp; 744 int ns, jid; 745 746 ns = 0; 747 for (jp = j->jp; jp < j->jp + j->njp; jp++) 748 if (!JP_RDTUN(jp)) 749 ns++; 750 if (ns == 0) 751 return 0; 752 sjp = setparams = alloca(++ns * sizeof(struct jailparam)); 753 if (jailparam_init(sjp, "jid") < 0 || 754 jailparam_import_raw(sjp, &j->jid, sizeof j->jid) < 0) { 755 jail_warnx(j, "%s", jail_errmsg); 756 failed(j); 757 return -1; 758 } 759 for (jp = j->jp; jp < j->jp + j->njp; jp++) 760 if (!JP_RDTUN(jp)) 761 *++sjp = *jp; 762 763 jid = jailparam_set_note(j, setparams, ns, JAIL_UPDATE); 764 if (jid < 0) { 765 jail_warnx(j, "%s", jail_errmsg); 766 failed(j); 767 } 768 jailparam_free(setparams, 1); 769 return jid; 770 } 771 772 /* 773 * Return if a jail set would change any create-only parameters. 774 */ 775 static int 776 rdtun_params(struct cfjail *j, int dofail) 777 { 778 struct jailparam *jp, *rtparams, *rtjp; 779 const void *jp_value; 780 size_t jp_valuelen; 781 int nrt, rval, bool_true; 782 783 if (j->flags & JF_RDTUN) 784 return 0; 785 j->flags |= JF_RDTUN; 786 nrt = 0; 787 for (jp = j->jp; jp < j->jp + j->njp; jp++) 788 if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) 789 nrt++; 790 if (nrt == 0) 791 return 0; 792 rtjp = rtparams = alloca(++nrt * sizeof(struct jailparam)); 793 if (jailparam_init(rtjp, "jid") < 0 || 794 jailparam_import_raw(rtjp, &j->jid, sizeof j->jid) < 0) { 795 jail_warnx(j, "%s", jail_errmsg); 796 exit(1); 797 } 798 for (jp = j->jp; jp < j->jp + j->njp; jp++) 799 if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) { 800 *++rtjp = *jp; 801 rtjp->jp_value = NULL; 802 } 803 rval = 0; 804 if (jailparam_get(rtparams, nrt, 0) > 0) { 805 rtjp = rtparams + 1; 806 for (jp = j->jp; rtjp < rtparams + nrt; jp++) { 807 if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) { 808 jp_value = jp->jp_value; 809 jp_valuelen = jp->jp_valuelen; 810 if (jp_value == NULL && jp_valuelen > 0) { 811 if (jp->jp_flags & (JP_BOOL | 812 JP_NOBOOL | JP_JAILSYS)) { 813 bool_true = 1; 814 jp_value = &bool_true; 815 jp_valuelen = sizeof(bool_true); 816 } else if ((jp->jp_ctltype & CTLTYPE) == 817 CTLTYPE_STRING) 818 jp_value = ""; 819 else 820 jp_valuelen = 0; 821 } 822 if (rtjp->jp_valuelen != jp_valuelen || 823 (CTLTYPE_STRING ? strncmp(rtjp->jp_value, 824 jp_value, jp_valuelen) 825 : memcmp(rtjp->jp_value, jp_value, 826 jp_valuelen))) { 827 if (dofail) { 828 jail_warnx(j, "%s cannot be " 829 "changed after creation", 830 jp->jp_name); 831 failed(j); 832 rval = -1; 833 } else 834 rval = 1; 835 break; 836 } 837 rtjp++; 838 } 839 } 840 } 841 for (rtjp = rtparams + 1; rtjp < rtparams + nrt; rtjp++) 842 rtjp->jp_name = NULL; 843 jailparam_free(rtparams, nrt); 844 return rval; 845 } 846 847 /* 848 * Get the jail's jid if it is running. 849 */ 850 static void 851 running_jid(struct cfjail *j) 852 { 853 struct iovec jiov[2]; 854 const char *pval; 855 char *ep; 856 int jid; 857 858 if ((pval = string_param(j->intparams[KP_JID]))) { 859 if (!(jid = strtol(pval, &ep, 10)) || *ep) { 860 j->jid = -1; 861 return; 862 } 863 jiov[0].iov_base = __DECONST(char *, "jid"); 864 jiov[0].iov_len = sizeof("jid"); 865 jiov[1].iov_base = &jid; 866 jiov[1].iov_len = sizeof(jid); 867 } else if ((pval = string_param(j->intparams[KP_NAME]))) { 868 jiov[0].iov_base = __DECONST(char *, "name"); 869 jiov[0].iov_len = sizeof("name"); 870 jiov[1].iov_len = strlen(pval) + 1; 871 jiov[1].iov_base = alloca(jiov[1].iov_len); 872 strcpy(jiov[1].iov_base, pval); 873 } else { 874 j->jid = -1; 875 return; 876 } 877 j->jid = jail_get(jiov, 2, 0); 878 } 879 880 static void 881 jail_quoted_warnx(const struct cfjail *j, const char *name_msg, 882 const char *noname_msg) 883 { 884 const char *pval; 885 886 if ((pval = j->name) || (pval = string_param(j->intparams[KP_JID])) || 887 (pval = string_param(j->intparams[KP_NAME]))) 888 warnx("\"%s\" %s", pval, name_msg); 889 else 890 warnx("%s", noname_msg); 891 } 892 893 /* 894 * Set jail parameters and possibly print them out. 895 */ 896 static int 897 jailparam_set_note(const struct cfjail *j, struct jailparam *jp, unsigned njp, 898 int flags) 899 { 900 char *value; 901 int jid; 902 unsigned i; 903 904 jid = jailparam_set(jp, njp, flags); 905 if (verbose > 0) { 906 jail_note(j, "jail_set(%s)", 907 (flags & (JAIL_CREATE | JAIL_UPDATE)) == JAIL_CREATE 908 ? "JAIL_CREATE" : "JAIL_UPDATE"); 909 for (i = 0; i < njp; i++) { 910 printf(" %s", jp[i].jp_name); 911 if (jp[i].jp_value == NULL) 912 continue; 913 putchar('='); 914 value = jailparam_export(jp + i); 915 if (value == NULL) 916 err(1, "jailparam_export"); 917 quoted_print(stdout, value); 918 free(value); 919 } 920 if (jid < 0) 921 printf(": %s", strerror(errno)); 922 printf("\n"); 923 } 924 return jid; 925 } 926 927 /* 928 * Print a jail record. 929 */ 930 static void 931 print_jail(FILE *fp, struct cfjail *j, int oldcl, int running) 932 { 933 struct cfparam *p; 934 int printsep; 935 936 if (oldcl) { 937 if (running) 938 fprintf(fp, "%d%s", j->jid, separator); 939 print_param(fp, j->intparams[KP_PATH], ',', 0); 940 fputs(separator, fp); 941 print_param(fp, j->intparams[KP_HOST_HOSTNAME], ',', 0); 942 fputs(separator, fp); 943 #ifdef INET 944 print_param(fp, j->intparams[KP_IP4_ADDR], ',', 0); 945 #ifdef INET6 946 if (j->intparams[KP_IP4_ADDR] && 947 !TAILQ_EMPTY(&j->intparams[KP_IP4_ADDR]->val) && 948 j->intparams[KP_IP6_ADDR] && 949 !TAILQ_EMPTY(&j->intparams[KP_IP6_ADDR]->val)) 950 putc(',', fp); 951 #endif 952 #endif 953 #ifdef INET6 954 print_param(fp, j->intparams[KP_IP6_ADDR], ',', 0); 955 #endif 956 fputs(separator, fp); 957 print_param(fp, j->intparams[IP_COMMAND], ' ', 0); 958 } else { 959 printsep = 0; 960 if (running) { 961 fprintf(fp, "jid=%d", j->jid); 962 printsep = 1; 963 } 964 TAILQ_FOREACH(p, &j->params, tq) 965 if (strcmp(p->name, "jid")) { 966 if (printsep) 967 fputs(separator, fp); 968 else 969 printsep = 1; 970 print_param(fp, p, ',', 1); 971 } 972 } 973 putc('\n', fp); 974 } 975 976 /* 977 * Exhibit list of all configured non-wildcard jails 978 */ 979 static void 980 show_jails(void) 981 { 982 struct cfjail *j; 983 984 TAILQ_FOREACH(j, &cfjails, tq) 985 print_jail(stdout, j, 0, 0); 986 } 987 988 /* 989 * Print a parameter value, or a name=value pair. 990 */ 991 static void 992 print_param(FILE *fp, const struct cfparam *p, int sep, int doname) 993 { 994 const struct cfstring *s, *ts; 995 996 if (doname) 997 fputs(p->name, fp); 998 if (p == NULL || TAILQ_EMPTY(&p->val)) 999 return; 1000 if (doname) 1001 putc('=', fp); 1002 TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { 1003 quoted_print(fp, s->s); 1004 if (ts != NULL) 1005 putc(sep, fp); 1006 } 1007 } 1008 1009 /* 1010 * Print a string with quotes around spaces. 1011 */ 1012 static void 1013 quoted_print(FILE *fp, char *str) 1014 { 1015 int c, qc; 1016 char *p = str; 1017 1018 qc = !*p ? '"' 1019 : strchr(p, '\'') ? '"' 1020 : strchr(p, '"') ? '\'' 1021 : strchr(p, ' ') || strchr(p, '\t') ? '"' 1022 : 0; 1023 if (qc) 1024 putc(qc, fp); 1025 while ((c = *p++)) { 1026 if (c == '\\' || c == qc) 1027 putc('\\', fp); 1028 putc(c, fp); 1029 } 1030 if (qc) 1031 putc(qc, fp); 1032 } 1033 1034 static void 1035 usage(void) 1036 { 1037 1038 (void)fprintf(stderr, 1039 "usage: jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n" 1040 " -[cmr] param=value ... [command=command ...]\n" 1041 " jail [-dqv] [-f file] -[cmr] [jail]\n" 1042 " jail [-qv] [-f file] -[rR] ['*' | jail ...]\n" 1043 " jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n" 1044 " [-n jailname] [-s securelevel]\n" 1045 " path hostname ip[,...] command ...\n" 1046 " jail [-f file] -e separator\n"); 1047 exit(1); 1048 } 1049