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
main(int argc,char ** argv)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
failed(struct cfjail * j)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 *
emalloc(size_t size)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 *
erealloc(void * ptr,size_t size)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 *
estrdup(const char * str)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
jail_note(const struct cfjail * j,const char * fmt,...)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
jail_warnx(const struct cfjail * j,const char * fmt,...)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
create_jail(struct cfjail * j)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
clear_persist(struct cfjail * j)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
update_jail(struct cfjail * j)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
rdtun_params(struct cfjail * j,int dofail)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
running_jid(struct cfjail * j)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
jail_quoted_warnx(const struct cfjail * j,const char * name_msg,const char * noname_msg)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
jailparam_set_note(const struct cfjail * j,struct jailparam * jp,unsigned njp,int flags)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
print_jail(FILE * fp,struct cfjail * j,int oldcl,int running)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
show_jails(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
print_param(FILE * fp,const struct cfparam * p,int sep,int doname)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
quoted_print(FILE * fp,char * str)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
usage(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