12671ee73SJamie Gritton /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31de7b4b8SPedro F. Giffuni * 4d8352076SJamie Gritton * Copyright (c) 2011 James Gritton 52671ee73SJamie Gritton * All rights reserved. 62671ee73SJamie Gritton * 72671ee73SJamie Gritton * Redistribution and use in source and binary forms, with or without 82671ee73SJamie Gritton * modification, are permitted provided that the following conditions 92671ee73SJamie Gritton * are met: 102671ee73SJamie Gritton * 1. Redistributions of source code must retain the above copyright 112671ee73SJamie Gritton * notice, this list of conditions and the following disclaimer. 122671ee73SJamie Gritton * 2. Redistributions in binary form must reproduce the above copyright 132671ee73SJamie Gritton * notice, this list of conditions and the following disclaimer in the 142671ee73SJamie Gritton * documentation and/or other materials provided with the distribution. 152671ee73SJamie Gritton * 162671ee73SJamie Gritton * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 172671ee73SJamie Gritton * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 182671ee73SJamie Gritton * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 192671ee73SJamie Gritton * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 202671ee73SJamie Gritton * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 212671ee73SJamie Gritton * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 222671ee73SJamie Gritton * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 232671ee73SJamie Gritton * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 242671ee73SJamie Gritton * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 252671ee73SJamie Gritton * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 262671ee73SJamie Gritton * SUCH DAMAGE. 272671ee73SJamie Gritton */ 282671ee73SJamie Gritton 292671ee73SJamie Gritton #include <sys/types.h> 30466df976SKyle Evans #include <sys/cpuset.h> 312671ee73SJamie Gritton #include <sys/event.h> 325264032fSJamie Gritton #include <sys/mount.h> 332671ee73SJamie Gritton #include <sys/stat.h> 342671ee73SJamie Gritton #include <sys/sysctl.h> 352671ee73SJamie Gritton #include <sys/user.h> 362671ee73SJamie Gritton #include <sys/wait.h> 372671ee73SJamie Gritton 382671ee73SJamie Gritton #include <err.h> 392671ee73SJamie Gritton #include <errno.h> 402671ee73SJamie Gritton #include <fcntl.h> 412671ee73SJamie Gritton #include <kvm.h> 422671ee73SJamie Gritton #include <login_cap.h> 432671ee73SJamie Gritton #include <paths.h> 442671ee73SJamie Gritton #include <pwd.h> 452671ee73SJamie Gritton #include <signal.h> 462671ee73SJamie Gritton #include <stdio.h> 472671ee73SJamie Gritton #include <stdlib.h> 482671ee73SJamie Gritton #include <string.h> 492671ee73SJamie Gritton #include <unistd.h> 50a99d8210SJamie Gritton #include <vis.h> 512671ee73SJamie Gritton 522671ee73SJamie Gritton #include "jailp.h" 532671ee73SJamie Gritton 542671ee73SJamie Gritton #define DEFAULT_STOP_TIMEOUT 10 552671ee73SJamie Gritton #define PHASH_SIZE 256 562671ee73SJamie Gritton 572671ee73SJamie Gritton LIST_HEAD(phhead, phash); 582671ee73SJamie Gritton 592671ee73SJamie Gritton struct phash { 602671ee73SJamie Gritton LIST_ENTRY(phash) le; 612671ee73SJamie Gritton struct cfjail *j; 622671ee73SJamie Gritton pid_t pid; 632671ee73SJamie Gritton }; 642671ee73SJamie Gritton 65aa02af54SJamie Gritton int paralimit = -1; 66aa02af54SJamie Gritton 672671ee73SJamie Gritton extern char **environ; 682671ee73SJamie Gritton 693b40332cSJamie Gritton static int run_command(struct cfjail *j); 70e5935495SJamie Gritton static int add_proc(struct cfjail *j, pid_t pid); 712671ee73SJamie Gritton static void clear_procs(struct cfjail *j); 722671ee73SJamie Gritton static struct cfjail *find_proc(pid_t pid); 73aa02af54SJamie Gritton static int term_procs(struct cfjail *j); 743b40332cSJamie Gritton static int get_user_info(struct cfjail *j, const char *username, 753b40332cSJamie Gritton const struct passwd **pwdp, login_cap_t **lcapp); 768ebbf0e2SJamie Gritton static int check_path(struct cfjail *j, const char *pname, const char *path, 775264032fSJamie Gritton int isfile, const char *umount_type); 782671ee73SJamie Gritton 792671ee73SJamie Gritton static struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping); 802671ee73SJamie Gritton static struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable); 81c6eff841SJamie Gritton static struct cfstring dummystring = { .len = 1 }; 822671ee73SJamie Gritton static struct phhead phash[PHASH_SIZE]; 832671ee73SJamie Gritton static int kq; 842671ee73SJamie Gritton 85466df976SKyle Evans static cpusetid_t 86466df976SKyle Evans root_cpuset_id(void) 87466df976SKyle Evans { 88466df976SKyle Evans static cpusetid_t setid = CPUSET_INVALID; 89466df976SKyle Evans static int error; 90466df976SKyle Evans 91466df976SKyle Evans /* Only try to get the cpuset once. */ 92466df976SKyle Evans if (error == 0 && setid == CPUSET_INVALID) 93466df976SKyle Evans error = cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, &setid); 94466df976SKyle Evans if (error != 0) 95466df976SKyle Evans return (CPUSET_INVALID); 96466df976SKyle Evans return (setid); 97466df976SKyle Evans } 98466df976SKyle Evans 992671ee73SJamie Gritton /* 1003b40332cSJamie Gritton * Run the next command associated with a jail. 1012671ee73SJamie Gritton */ 1022671ee73SJamie Gritton int 1033b40332cSJamie Gritton next_command(struct cfjail *j) 1042671ee73SJamie Gritton { 1053b40332cSJamie Gritton enum intparam comparam; 106bea2eefcSJamie Gritton int create_failed, stopping; 1072671ee73SJamie Gritton 10860080230SJamie Gritton if (paralimit == 0) { 1094c86c0faSJamie Gritton if (j->flags & JF_FROM_RUNQ) 1104c86c0faSJamie Gritton requeue_head(j, &runnable); 1114c86c0faSJamie Gritton else 11260080230SJamie Gritton requeue(j, &runnable); 11360080230SJamie Gritton return 1; 11460080230SJamie Gritton } 1154c86c0faSJamie Gritton j->flags &= ~JF_FROM_RUNQ; 1163b40332cSJamie Gritton create_failed = (j->flags & (JF_STOP | JF_FAILED)) == JF_FAILED; 117bea2eefcSJamie Gritton stopping = (j->flags & JF_STOP) != 0; 11860080230SJamie Gritton comparam = *j->comparam; 11960080230SJamie Gritton for (;;) { 1203b40332cSJamie Gritton if (j->comstring == NULL) { 12160080230SJamie Gritton j->comparam += create_failed ? -1 : 1; 12260080230SJamie Gritton switch ((comparam = *j->comparam)) { 1236fcbac3cSJamie Gritton case IP__NULL: 12460080230SJamie Gritton return 0; 125aa02af54SJamie Gritton case IP_MOUNT_DEVFS: 126aa02af54SJamie Gritton if (!bool_param(j->intparams[IP_MOUNT_DEVFS])) 1273b40332cSJamie Gritton continue; 128fbd868c9SHiroki Sato j->comstring = &dummystring; 129fbd868c9SHiroki Sato break; 130fbd868c9SHiroki Sato case IP_MOUNT_FDESCFS: 131fbd868c9SHiroki Sato if (!bool_param(j->intparams[IP_MOUNT_FDESCFS])) 132fbd868c9SHiroki Sato continue; 133fbd868c9SHiroki Sato j->comstring = &dummystring; 13407a7869fSJamie Gritton break; 13507a7869fSJamie Gritton case IP_MOUNT_PROCFS: 13607a7869fSJamie Gritton if (!bool_param(j->intparams[IP_MOUNT_PROCFS])) 13707a7869fSJamie Gritton continue; 13807a7869fSJamie Gritton j->comstring = &dummystring; 13907a7869fSJamie Gritton break; 1402b00f7baSJamie Gritton case IP__OP: 141aa02af54SJamie Gritton case IP_STOP_TIMEOUT: 1423b40332cSJamie Gritton j->comstring = &dummystring; 143aa02af54SJamie Gritton break; 144aa02af54SJamie Gritton default: 145aa02af54SJamie Gritton if (j->intparams[comparam] == NULL) 1463b40332cSJamie Gritton continue; 147bea2eefcSJamie Gritton j->comstring = create_failed || (stopping && 148bea2eefcSJamie Gritton (j->intparams[comparam]->flags & PF_REV)) 1493b40332cSJamie Gritton ? TAILQ_LAST(&j->intparams[comparam]->val, 1503b40332cSJamie Gritton cfstrings) 1513b40332cSJamie Gritton : TAILQ_FIRST(&j->intparams[comparam]->val); 1522671ee73SJamie Gritton } 15360080230SJamie Gritton } else { 154c6eff841SJamie Gritton j->comstring = j->comstring == &dummystring ? NULL : 155bea2eefcSJamie Gritton create_failed || (stopping && 156bea2eefcSJamie Gritton (j->intparams[comparam]->flags & PF_REV)) 1573b40332cSJamie Gritton ? TAILQ_PREV(j->comstring, cfstrings, tq) 15860080230SJamie Gritton : TAILQ_NEXT(j->comstring, tq); 15960080230SJamie Gritton } 16060080230SJamie Gritton if (j->comstring == NULL || j->comstring->len == 0 || 16160080230SJamie Gritton (create_failed && (comparam == IP_EXEC_PRESTART || 162f6c0e63bSAlexander Leidinger comparam == IP_EXEC_CREATED || comparam == IP_EXEC_START || 16366005c45SRyan Moeller comparam == IP_COMMAND || comparam == IP_EXEC_POSTSTART || 16466005c45SRyan Moeller comparam == IP_EXEC_PREPARE))) 1653b40332cSJamie Gritton continue; 16660080230SJamie Gritton switch (run_command(j)) { 16760080230SJamie Gritton case -1: 16860080230SJamie Gritton failed(j); 16960080230SJamie Gritton /* FALLTHROUGH */ 17060080230SJamie Gritton case 1: 1712671ee73SJamie Gritton return 1; 1722671ee73SJamie Gritton } 1732671ee73SJamie Gritton } 174aa02af54SJamie Gritton } 1752671ee73SJamie Gritton 1762671ee73SJamie Gritton /* 1772671ee73SJamie Gritton * Check command exit status 1782671ee73SJamie Gritton */ 1792671ee73SJamie Gritton int 180aa02af54SJamie Gritton finish_command(struct cfjail *j) 1812671ee73SJamie Gritton { 1824c86c0faSJamie Gritton struct cfjail *rj; 1832671ee73SJamie Gritton int error; 1842671ee73SJamie Gritton 185aa02af54SJamie Gritton if (!(j->flags & JF_SLEEPQ)) 1862671ee73SJamie Gritton return 0; 187aa02af54SJamie Gritton j->flags &= ~JF_SLEEPQ; 1884c86c0faSJamie Gritton if (*j->comparam == IP_STOP_TIMEOUT) { 189eadec913SJamie Gritton j->flags &= ~JF_TIMEOUT; 190eadec913SJamie Gritton j->pstatus = 0; 191eadec913SJamie Gritton return 0; 192eadec913SJamie Gritton } 193aa02af54SJamie Gritton paralimit++; 1944c86c0faSJamie Gritton if (!TAILQ_EMPTY(&runnable)) { 1954c86c0faSJamie Gritton rj = TAILQ_FIRST(&runnable); 1964c86c0faSJamie Gritton rj->flags |= JF_FROM_RUNQ; 1974c86c0faSJamie Gritton requeue(rj, &ready); 1984c86c0faSJamie Gritton } 1992671ee73SJamie Gritton error = 0; 2002671ee73SJamie Gritton if (j->flags & JF_TIMEOUT) { 2012671ee73SJamie Gritton j->flags &= ~JF_TIMEOUT; 2023b40332cSJamie Gritton if (*j->comparam != IP_STOP_TIMEOUT) { 2032671ee73SJamie Gritton jail_warnx(j, "%s: timed out", j->comline); 2042671ee73SJamie Gritton failed(j); 2052671ee73SJamie Gritton error = -1; 2062671ee73SJamie Gritton } else if (verbose > 0) 2072671ee73SJamie Gritton jail_note(j, "timed out\n"); 2082671ee73SJamie Gritton } else if (j->pstatus != 0) { 2092671ee73SJamie Gritton if (WIFSIGNALED(j->pstatus)) 2102671ee73SJamie Gritton jail_warnx(j, "%s: exited on signal %d", 2112671ee73SJamie Gritton j->comline, WTERMSIG(j->pstatus)); 2122671ee73SJamie Gritton else 2132671ee73SJamie Gritton jail_warnx(j, "%s: failed", j->comline); 214aa02af54SJamie Gritton j->pstatus = 0; 2152671ee73SJamie Gritton failed(j); 2162671ee73SJamie Gritton error = -1; 2172671ee73SJamie Gritton } 2182671ee73SJamie Gritton free(j->comline); 219aa02af54SJamie Gritton j->comline = NULL; 2202671ee73SJamie Gritton return error; 2212671ee73SJamie Gritton } 2222671ee73SJamie Gritton 2232671ee73SJamie Gritton /* 22460080230SJamie Gritton * Check for finished processes or timeouts. 2252671ee73SJamie Gritton */ 2262671ee73SJamie Gritton struct cfjail * 2272671ee73SJamie Gritton next_proc(int nonblock) 2282671ee73SJamie Gritton { 2292671ee73SJamie Gritton struct kevent ke; 2302671ee73SJamie Gritton struct timespec ts; 2312671ee73SJamie Gritton struct timespec *tsp; 2322671ee73SJamie Gritton struct cfjail *j; 2332671ee73SJamie Gritton 2342671ee73SJamie Gritton if (!TAILQ_EMPTY(&sleeping)) { 2352671ee73SJamie Gritton again: 2362671ee73SJamie Gritton tsp = NULL; 2372671ee73SJamie Gritton if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) { 2382671ee73SJamie Gritton clock_gettime(CLOCK_REALTIME, &ts); 2392671ee73SJamie Gritton ts.tv_sec = j->timeout.tv_sec - ts.tv_sec; 2402671ee73SJamie Gritton ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec; 2412671ee73SJamie Gritton if (ts.tv_nsec < 0) { 2422671ee73SJamie Gritton ts.tv_sec--; 2432671ee73SJamie Gritton ts.tv_nsec += 1000000000; 2442671ee73SJamie Gritton } 2452671ee73SJamie Gritton if (ts.tv_sec < 0 || 2462671ee73SJamie Gritton (ts.tv_sec == 0 && ts.tv_nsec == 0)) { 2472671ee73SJamie Gritton j->flags |= JF_TIMEOUT; 2482671ee73SJamie Gritton clear_procs(j); 2492671ee73SJamie Gritton return j; 2502671ee73SJamie Gritton } 2512671ee73SJamie Gritton tsp = &ts; 2522671ee73SJamie Gritton } 2532671ee73SJamie Gritton if (nonblock) { 2542671ee73SJamie Gritton ts.tv_sec = 0; 2552671ee73SJamie Gritton ts.tv_nsec = 0; 2562671ee73SJamie Gritton tsp = &ts; 2572671ee73SJamie Gritton } 2582671ee73SJamie Gritton switch (kevent(kq, NULL, 0, &ke, 1, tsp)) { 2592671ee73SJamie Gritton case -1: 2602671ee73SJamie Gritton if (errno != EINTR) 2612671ee73SJamie Gritton err(1, "kevent"); 2622671ee73SJamie Gritton goto again; 2632671ee73SJamie Gritton case 0: 2642671ee73SJamie Gritton if (!nonblock) { 2652671ee73SJamie Gritton j = TAILQ_FIRST(&sleeping); 2662671ee73SJamie Gritton j->flags |= JF_TIMEOUT; 2672671ee73SJamie Gritton clear_procs(j); 2682671ee73SJamie Gritton return j; 2692671ee73SJamie Gritton } 2702671ee73SJamie Gritton break; 2712671ee73SJamie Gritton case 1: 2722671ee73SJamie Gritton (void)waitpid(ke.ident, NULL, WNOHANG); 2732671ee73SJamie Gritton if ((j = find_proc(ke.ident))) { 2742671ee73SJamie Gritton j->pstatus = ke.data; 2752671ee73SJamie Gritton return j; 2762671ee73SJamie Gritton } 2772671ee73SJamie Gritton goto again; 2782671ee73SJamie Gritton } 2792671ee73SJamie Gritton } 2802671ee73SJamie Gritton return NULL; 2812671ee73SJamie Gritton } 2822671ee73SJamie Gritton 2832671ee73SJamie Gritton /* 2844c86c0faSJamie Gritton * Run a single command for a jail, possibly inside the jail. 2853b40332cSJamie Gritton */ 286a6486f60SJamie Gritton static int 2873b40332cSJamie Gritton run_command(struct cfjail *j) 2883b40332cSJamie Gritton { 2893b40332cSJamie Gritton const struct passwd *pwd; 2903b40332cSJamie Gritton const struct cfstring *comstring, *s; 2913b40332cSJamie Gritton login_cap_t *lcap; 292d031802bSJamie Gritton const char **argv; 293d031802bSJamie Gritton char *acs, *cs, *comcs, *devpath; 294e0dfe185SAlexander Leidinger const char *jidstr, *conslog, *fmt, *path, *ruleset, *term, *username; 2953b40332cSJamie Gritton enum intparam comparam; 296e0dfe185SAlexander Leidinger size_t comlen, ret; 2973b40332cSJamie Gritton pid_t pid; 298466df976SKyle Evans cpusetid_t setid; 2993b40332cSJamie Gritton int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout; 3005fb611c2SJamie Gritton #if defined(INET) || defined(INET6) 301b22b6abdSSteven Hartland char *addr, *extrap, *p, *val; 3025fb611c2SJamie Gritton #endif 3033b40332cSJamie Gritton 3043b40332cSJamie Gritton static char *cleanenv; 3053b40332cSJamie Gritton 3062b00f7baSJamie Gritton /* Perform some operations that aren't actually commands */ 3072b00f7baSJamie Gritton comparam = *j->comparam; 3082b00f7baSJamie Gritton down = j->flags & (JF_STOP | JF_FAILED); 3092b00f7baSJamie Gritton switch (comparam) { 3102b00f7baSJamie Gritton case IP_STOP_TIMEOUT: 3112b00f7baSJamie Gritton return term_procs(j); 3122b00f7baSJamie Gritton 3132b00f7baSJamie Gritton case IP__OP: 3142b00f7baSJamie Gritton if (down) { 3151ca35de4SJamie Gritton if (jail_remove(j->jid) < 0 && errno == EPERM) { 3161ca35de4SJamie Gritton jail_warnx(j, "jail_remove: %s", 3171ca35de4SJamie Gritton strerror(errno)); 3181ca35de4SJamie Gritton return -1; 3191ca35de4SJamie Gritton } 320eadec913SJamie Gritton if (verbose > 0 || (verbose == 0 && (j->flags & JF_STOP 3212b00f7baSJamie Gritton ? note_remove : j->name != NULL))) 3222b00f7baSJamie Gritton jail_note(j, "removed\n"); 3232b00f7baSJamie Gritton j->jid = -1; 3242b00f7baSJamie Gritton if (j->flags & JF_STOP) 3252b00f7baSJamie Gritton dep_done(j, DF_LIGHT); 3262b00f7baSJamie Gritton else 3272b00f7baSJamie Gritton j->flags &= ~JF_PERSIST; 3282b00f7baSJamie Gritton } else { 32960080230SJamie Gritton if (create_jail(j) < 0) 3302b00f7baSJamie Gritton return -1; 331a6486f60SJamie Gritton if (iflag) 332a6486f60SJamie Gritton printf("%d\n", j->jid); 3332b00f7baSJamie Gritton if (verbose >= 0 && (j->name || verbose > 0)) 3342b00f7baSJamie Gritton jail_note(j, "created\n"); 3352b00f7baSJamie Gritton dep_done(j, DF_LIGHT); 3362b00f7baSJamie Gritton } 33760080230SJamie Gritton return 0; 338e16fb8fbSJamie Gritton 339e16fb8fbSJamie Gritton default: ; 3402b00f7baSJamie Gritton } 3413b40332cSJamie Gritton /* 3423b40332cSJamie Gritton * Collect exec arguments. Internal commands for network and 3433b40332cSJamie Gritton * mounting build their own argument lists. 3443b40332cSJamie Gritton */ 3453b40332cSJamie Gritton comstring = j->comstring; 3463b40332cSJamie Gritton bg = 0; 3473b40332cSJamie Gritton switch (comparam) { 3485fb611c2SJamie Gritton #ifdef INET 3493b40332cSJamie Gritton case IP__IP4_IFADDR: 350b22b6abdSSteven Hartland argc = 0; 351b22b6abdSSteven Hartland val = alloca(strlen(comstring->s) + 1); 352b22b6abdSSteven Hartland strcpy(val, comstring->s); 353b22b6abdSSteven Hartland cs = val; 354b22b6abdSSteven Hartland extrap = NULL; 355b22b6abdSSteven Hartland while ((p = strchr(cs, ' ')) != NULL && strlen(p) > 1) { 356b22b6abdSSteven Hartland if (extrap == NULL) { 357b22b6abdSSteven Hartland *p = '\0'; 358b22b6abdSSteven Hartland extrap = p + 1; 359b22b6abdSSteven Hartland } 360b22b6abdSSteven Hartland cs = p + 1; 361b22b6abdSSteven Hartland argc++; 362b22b6abdSSteven Hartland } 363b22b6abdSSteven Hartland 364b22b6abdSSteven Hartland argv = alloca((8 + argc) * sizeof(char *)); 365d031802bSJamie Gritton argv[0] = _PATH_IFCONFIG; 366b22b6abdSSteven Hartland if ((cs = strchr(val, '|'))) { 367d031802bSJamie Gritton argv[1] = acs = alloca(cs - val + 1); 368d031802bSJamie Gritton strlcpy(acs, val, cs - val + 1); 3693b40332cSJamie Gritton addr = cs + 1; 3703b40332cSJamie Gritton } else { 371d031802bSJamie Gritton argv[1] = string_param(j->intparams[IP_INTERFACE]); 372b22b6abdSSteven Hartland addr = val; 3733b40332cSJamie Gritton } 374d031802bSJamie Gritton argv[2] = "inet"; 3753b40332cSJamie Gritton if (!(cs = strchr(addr, '/'))) { 3763b40332cSJamie Gritton argv[3] = addr; 377d031802bSJamie Gritton argv[4] = "netmask"; 378d031802bSJamie Gritton argv[5] = "255.255.255.255"; 3793b40332cSJamie Gritton argc = 6; 3803b40332cSJamie Gritton } else if (strchr(cs + 1, '.')) { 381d031802bSJamie Gritton argv[3] = acs = alloca(cs - addr + 1); 382d031802bSJamie Gritton strlcpy(acs, addr, cs - addr + 1); 383d031802bSJamie Gritton argv[4] = "netmask"; 384d031802bSJamie Gritton argv[5] = cs + 1; 3853b40332cSJamie Gritton argc = 6; 3863b40332cSJamie Gritton } else { 3873b40332cSJamie Gritton argv[3] = addr; 3883b40332cSJamie Gritton argc = 4; 3893b40332cSJamie Gritton } 390b22b6abdSSteven Hartland 391bd24e861SEugene Grosbein if (!down && extrap != NULL) { 392d031802bSJamie Gritton for (cs = strtok(extrap, " "); cs; 393d031802bSJamie Gritton cs = strtok(NULL, " ")) { 394b22b6abdSSteven Hartland size_t len = strlen(cs) + 1; 395d031802bSJamie Gritton argv[argc++] = acs = alloca(len); 396d031802bSJamie Gritton strlcpy(acs, cs, len); 397b22b6abdSSteven Hartland } 398b22b6abdSSteven Hartland } 399b22b6abdSSteven Hartland 400d031802bSJamie Gritton argv[argc] = down ? "-alias" : "alias"; 4013b40332cSJamie Gritton argv[argc + 1] = NULL; 4023b40332cSJamie Gritton break; 4035fb611c2SJamie Gritton #endif 4043b40332cSJamie Gritton 4053b40332cSJamie Gritton #ifdef INET6 4063b40332cSJamie Gritton case IP__IP6_IFADDR: 407b22b6abdSSteven Hartland argc = 0; 408b22b6abdSSteven Hartland val = alloca(strlen(comstring->s) + 1); 409b22b6abdSSteven Hartland strcpy(val, comstring->s); 410b22b6abdSSteven Hartland cs = val; 411b22b6abdSSteven Hartland extrap = NULL; 412b22b6abdSSteven Hartland while ((p = strchr(cs, ' ')) != NULL && strlen(p) > 1) { 413b22b6abdSSteven Hartland if (extrap == NULL) { 414b22b6abdSSteven Hartland *p = '\0'; 415b22b6abdSSteven Hartland extrap = p + 1; 416b22b6abdSSteven Hartland } 417b22b6abdSSteven Hartland cs = p + 1; 418b22b6abdSSteven Hartland argc++; 419b22b6abdSSteven Hartland } 420b22b6abdSSteven Hartland 421b22b6abdSSteven Hartland argv = alloca((8 + argc) * sizeof(char *)); 422d031802bSJamie Gritton argv[0] = _PATH_IFCONFIG; 423b22b6abdSSteven Hartland if ((cs = strchr(val, '|'))) { 424d031802bSJamie Gritton argv[1] = acs = alloca(cs - val + 1); 425d031802bSJamie Gritton strlcpy(acs, val, cs - val + 1); 4263b40332cSJamie Gritton addr = cs + 1; 4273b40332cSJamie Gritton } else { 428d031802bSJamie Gritton argv[1] = string_param(j->intparams[IP_INTERFACE]); 429b22b6abdSSteven Hartland addr = val; 4303b40332cSJamie Gritton } 431d031802bSJamie Gritton argv[2] = "inet6"; 4323b40332cSJamie Gritton argv[3] = addr; 4333b40332cSJamie Gritton if (!(cs = strchr(addr, '/'))) { 434d031802bSJamie Gritton argv[4] = "prefixlen"; 435d031802bSJamie Gritton argv[5] = "128"; 4363b40332cSJamie Gritton argc = 6; 4373b40332cSJamie Gritton } else 4383b40332cSJamie Gritton argc = 4; 439b22b6abdSSteven Hartland 440e8d7ae91SJamie Gritton if (!down && extrap != NULL) { 441d031802bSJamie Gritton for (cs = strtok(extrap, " "); cs; 442d031802bSJamie Gritton cs = strtok(NULL, " ")) { 443b22b6abdSSteven Hartland size_t len = strlen(cs) + 1; 444d031802bSJamie Gritton argv[argc++] = acs = alloca(len); 445d031802bSJamie Gritton strlcpy(acs, cs, len); 446b22b6abdSSteven Hartland } 447b22b6abdSSteven Hartland } 448b22b6abdSSteven Hartland 449d031802bSJamie Gritton argv[argc] = down ? "-alias" : "alias"; 4503b40332cSJamie Gritton argv[argc + 1] = NULL; 4513b40332cSJamie Gritton break; 4523b40332cSJamie Gritton #endif 4533b40332cSJamie Gritton 4543b40332cSJamie Gritton case IP_VNET_INTERFACE: 4553b40332cSJamie Gritton argv = alloca(5 * sizeof(char *)); 456d031802bSJamie Gritton argv[0] = _PATH_IFCONFIG; 4573b40332cSJamie Gritton argv[1] = comstring->s; 458d031802bSJamie Gritton argv[2] = down ? "-vnet" : "vnet"; 4593b40332cSJamie Gritton jidstr = string_param(j->intparams[KP_JID]); 460d031802bSJamie Gritton argv[3] = jidstr ? jidstr : string_param(j->intparams[KP_NAME]); 4613b40332cSJamie Gritton argv[4] = NULL; 4623b40332cSJamie Gritton break; 4633b40332cSJamie Gritton 4643b40332cSJamie Gritton case IP_MOUNT: 4653b40332cSJamie Gritton case IP__MOUNT_FROM_FSTAB: 4663b40332cSJamie Gritton argv = alloca(8 * sizeof(char *)); 4673b40332cSJamie Gritton comcs = alloca(comstring->len + 1); 4683b40332cSJamie Gritton strcpy(comcs, comstring->s); 4693b40332cSJamie Gritton argc = 0; 4703b40332cSJamie Gritton for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4; 471a99d8210SJamie Gritton cs = strtok(NULL, " \t\f\v\r\n")) { 472a99d8210SJamie Gritton if (argc <= 1 && strunvis(cs, cs) < 0) { 473a99d8210SJamie Gritton jail_warnx(j, "%s: %s: fstab parse error", 474a99d8210SJamie Gritton j->intparams[comparam]->name, comstring->s); 475a99d8210SJamie Gritton return -1; 476a99d8210SJamie Gritton } 4773b40332cSJamie Gritton argv[argc++] = cs; 478a99d8210SJamie Gritton } 4793b40332cSJamie Gritton if (argc == 0) 4803b40332cSJamie Gritton return 0; 4813b40332cSJamie Gritton if (argc < 3) { 4823b40332cSJamie Gritton jail_warnx(j, "%s: %s: missing information", 4833b40332cSJamie Gritton j->intparams[comparam]->name, comstring->s); 4843b40332cSJamie Gritton return -1; 4853b40332cSJamie Gritton } 4863b40332cSJamie Gritton if (check_path(j, j->intparams[comparam]->name, argv[1], 0, 48760080230SJamie Gritton down ? argv[2] : NULL) < 0) 4883b40332cSJamie Gritton return -1; 4893b40332cSJamie Gritton if (down) { 4903b40332cSJamie Gritton argv[4] = NULL; 4913b40332cSJamie Gritton argv[3] = argv[1]; 492d031802bSJamie Gritton argv[0] = "/sbin/umount"; 4933b40332cSJamie Gritton } else { 4943b40332cSJamie Gritton if (argc == 4) { 4953b40332cSJamie Gritton argv[7] = NULL; 4963b40332cSJamie Gritton argv[6] = argv[1]; 4973b40332cSJamie Gritton argv[5] = argv[0]; 4983b40332cSJamie Gritton argv[4] = argv[3]; 499d031802bSJamie Gritton argv[3] = "-o"; 5003b40332cSJamie Gritton } else { 5013b40332cSJamie Gritton argv[5] = NULL; 5023b40332cSJamie Gritton argv[4] = argv[1]; 5033b40332cSJamie Gritton argv[3] = argv[0]; 5043b40332cSJamie Gritton } 505d031802bSJamie Gritton argv[0] = _PATH_MOUNT; 5063b40332cSJamie Gritton } 507d031802bSJamie Gritton argv[1] = "-t"; 5083b40332cSJamie Gritton break; 5093b40332cSJamie Gritton 5103b40332cSJamie Gritton case IP_MOUNT_DEVFS: 5110c4d49e9SJamie Gritton argv = alloca(7 * sizeof(char *)); 5123b40332cSJamie Gritton path = string_param(j->intparams[KP_PATH]); 5133b40332cSJamie Gritton if (path == NULL) { 514be659d72SDmitry Morozovsky jail_warnx(j, "mount.devfs: no jail root path defined"); 5153b40332cSJamie Gritton return -1; 5163b40332cSJamie Gritton } 5173b40332cSJamie Gritton devpath = alloca(strlen(path) + 5); 5183b40332cSJamie Gritton sprintf(devpath, "%s/dev", path); 5193b40332cSJamie Gritton if (check_path(j, "mount.devfs", devpath, 0, 52060080230SJamie Gritton down ? "devfs" : NULL) < 0) 5213b40332cSJamie Gritton return -1; 5223b40332cSJamie Gritton if (down) { 523d031802bSJamie Gritton argv[0] = "/sbin/umount"; 5243b40332cSJamie Gritton argv[1] = devpath; 5253b40332cSJamie Gritton argv[2] = NULL; 5263b40332cSJamie Gritton } else { 527d031802bSJamie Gritton argv[0] = _PATH_MOUNT; 528d031802bSJamie Gritton argv[1] = "-t"; 529d031802bSJamie Gritton argv[2] = "devfs"; 5300c4d49e9SJamie Gritton ruleset = string_param(j->intparams[KP_DEVFS_RULESET]); 5310c4d49e9SJamie Gritton if (!ruleset) 5320c4d49e9SJamie Gritton ruleset = "4"; /* devfsrules_jail */ 533d031802bSJamie Gritton argv[3] = acs = alloca(11 + strlen(ruleset)); 534d031802bSJamie Gritton sprintf(acs, "-oruleset=%s", ruleset); 535d031802bSJamie Gritton argv[4] = "."; 5360c4d49e9SJamie Gritton argv[5] = devpath; 5370c4d49e9SJamie Gritton argv[6] = NULL; 5383b40332cSJamie Gritton } 5393b40332cSJamie Gritton break; 5403b40332cSJamie Gritton 541fbd868c9SHiroki Sato case IP_MOUNT_FDESCFS: 542fbd868c9SHiroki Sato argv = alloca(7 * sizeof(char *)); 543fbd868c9SHiroki Sato path = string_param(j->intparams[KP_PATH]); 544fbd868c9SHiroki Sato if (path == NULL) { 545be659d72SDmitry Morozovsky jail_warnx(j, "mount.fdescfs: no jail root path defined"); 546fbd868c9SHiroki Sato return -1; 547fbd868c9SHiroki Sato } 548fbd868c9SHiroki Sato devpath = alloca(strlen(path) + 8); 549fbd868c9SHiroki Sato sprintf(devpath, "%s/dev/fd", path); 550fbd868c9SHiroki Sato if (check_path(j, "mount.fdescfs", devpath, 0, 551fbd868c9SHiroki Sato down ? "fdescfs" : NULL) < 0) 552fbd868c9SHiroki Sato return -1; 553fbd868c9SHiroki Sato if (down) { 554d031802bSJamie Gritton argv[0] = "/sbin/umount"; 555fbd868c9SHiroki Sato argv[1] = devpath; 556fbd868c9SHiroki Sato argv[2] = NULL; 557fbd868c9SHiroki Sato } else { 558d031802bSJamie Gritton argv[0] = _PATH_MOUNT; 559d031802bSJamie Gritton argv[1] = "-t"; 560d031802bSJamie Gritton argv[2] = "fdescfs"; 561d031802bSJamie Gritton argv[3] = "."; 562fbd868c9SHiroki Sato argv[4] = devpath; 563fbd868c9SHiroki Sato argv[5] = NULL; 564fbd868c9SHiroki Sato } 565fbd868c9SHiroki Sato break; 566fbd868c9SHiroki Sato 56707a7869fSJamie Gritton case IP_MOUNT_PROCFS: 56807a7869fSJamie Gritton argv = alloca(7 * sizeof(char *)); 56907a7869fSJamie Gritton path = string_param(j->intparams[KP_PATH]); 57007a7869fSJamie Gritton if (path == NULL) { 571be659d72SDmitry Morozovsky jail_warnx(j, "mount.procfs: no jail root path defined"); 57207a7869fSJamie Gritton return -1; 57307a7869fSJamie Gritton } 57407a7869fSJamie Gritton devpath = alloca(strlen(path) + 6); 57507a7869fSJamie Gritton sprintf(devpath, "%s/proc", path); 57607a7869fSJamie Gritton if (check_path(j, "mount.procfs", devpath, 0, 57707a7869fSJamie Gritton down ? "procfs" : NULL) < 0) 57807a7869fSJamie Gritton return -1; 57907a7869fSJamie Gritton if (down) { 58007a7869fSJamie Gritton argv[0] = "/sbin/umount"; 58107a7869fSJamie Gritton argv[1] = devpath; 58207a7869fSJamie Gritton argv[2] = NULL; 58307a7869fSJamie Gritton } else { 58407a7869fSJamie Gritton argv[0] = _PATH_MOUNT; 58507a7869fSJamie Gritton argv[1] = "-t"; 58607a7869fSJamie Gritton argv[2] = "procfs"; 58707a7869fSJamie Gritton argv[3] = "."; 58807a7869fSJamie Gritton argv[4] = devpath; 58907a7869fSJamie Gritton argv[5] = NULL; 59007a7869fSJamie Gritton } 59107a7869fSJamie Gritton break; 59207a7869fSJamie Gritton 593e0dfe185SAlexander Leidinger case IP_ZFS_DATASET: 594e0dfe185SAlexander Leidinger argv = alloca(4 * sizeof(char *)); 595e0dfe185SAlexander Leidinger jidstr = string_param(j->intparams[KP_JID]) ? 596e0dfe185SAlexander Leidinger string_param(j->intparams[KP_JID]) : 597e0dfe185SAlexander Leidinger string_param(j->intparams[KP_NAME]); 598e0dfe185SAlexander Leidinger fmt = "if [ $(/sbin/zfs get -H -o value jailed %s) = on ]; then /sbin/zfs jail %s %s || echo error, attaching %s to jail %s failed; else echo error, you need to set jailed=on for dataset %s; fi"; 599e0dfe185SAlexander Leidinger comlen = strlen(fmt) 600e0dfe185SAlexander Leidinger + 2 * strlen(jidstr) 601e0dfe185SAlexander Leidinger + 4 * comstring->len 602e0dfe185SAlexander Leidinger - 6 * 2 /* 6 * "%s" */ 603e0dfe185SAlexander Leidinger + 1; 604e0dfe185SAlexander Leidinger comcs = alloca(comlen); 605e0dfe185SAlexander Leidinger ret = snprintf(comcs, comlen, fmt, comstring->s, 606e0dfe185SAlexander Leidinger jidstr, comstring->s, comstring->s, jidstr, 607e0dfe185SAlexander Leidinger comstring->s); 608e0dfe185SAlexander Leidinger if (ret >= comlen) { 609e0dfe185SAlexander Leidinger jail_warnx(j, "internal error in ZFS dataset handling"); 610e0dfe185SAlexander Leidinger exit(1); 611e0dfe185SAlexander Leidinger } 612e0dfe185SAlexander Leidinger argv[0] = _PATH_BSHELL; 613e0dfe185SAlexander Leidinger argv[1] = "-c"; 614e0dfe185SAlexander Leidinger argv[2] = comcs; 615e0dfe185SAlexander Leidinger argv[3] = NULL; 616e0dfe185SAlexander Leidinger break; 617e0dfe185SAlexander Leidinger 6183b40332cSJamie Gritton case IP_COMMAND: 6193b40332cSJamie Gritton if (j->name != NULL) 6203b40332cSJamie Gritton goto default_command; 6213b40332cSJamie Gritton argc = 0; 6223b40332cSJamie Gritton TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq) 6233b40332cSJamie Gritton argc++; 6243b40332cSJamie Gritton argv = alloca((argc + 1) * sizeof(char *)); 6253b40332cSJamie Gritton argc = 0; 6263b40332cSJamie Gritton TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq) 6273b40332cSJamie Gritton argv[argc++] = s->s; 6283b40332cSJamie Gritton argv[argc] = NULL; 629c6eff841SJamie Gritton j->comstring = &dummystring; 6303b40332cSJamie Gritton break; 6313b40332cSJamie Gritton 6323b40332cSJamie Gritton default: 6333b40332cSJamie Gritton default_command: 6343b40332cSJamie Gritton if ((cs = strpbrk(comstring->s, "!\"$&'()*;<>?[\\]`{|}~")) && 6353b40332cSJamie Gritton !(cs[0] == '&' && cs[1] == '\0')) { 6363b40332cSJamie Gritton argv = alloca(4 * sizeof(char *)); 637d031802bSJamie Gritton argv[0] = _PATH_BSHELL; 638d031802bSJamie Gritton argv[1] = "-c"; 6393b40332cSJamie Gritton argv[2] = comstring->s; 6403b40332cSJamie Gritton argv[3] = NULL; 6413b40332cSJamie Gritton } else { 6423b40332cSJamie Gritton if (cs) { 6433b40332cSJamie Gritton *cs = 0; 6443b40332cSJamie Gritton bg = 1; 6453b40332cSJamie Gritton } 6463b40332cSJamie Gritton comcs = alloca(comstring->len + 1); 6473b40332cSJamie Gritton strcpy(comcs, comstring->s); 6483b40332cSJamie Gritton argc = 0; 6493b40332cSJamie Gritton for (cs = strtok(comcs, " \t\f\v\r\n"); cs; 6503b40332cSJamie Gritton cs = strtok(NULL, " \t\f\v\r\n")) 6513b40332cSJamie Gritton argc++; 6523b40332cSJamie Gritton argv = alloca((argc + 1) * sizeof(char *)); 6533b40332cSJamie Gritton strcpy(comcs, comstring->s); 6543b40332cSJamie Gritton argc = 0; 6553b40332cSJamie Gritton for (cs = strtok(comcs, " \t\f\v\r\n"); cs; 6563b40332cSJamie Gritton cs = strtok(NULL, " \t\f\v\r\n")) 6573b40332cSJamie Gritton argv[argc++] = cs; 6583b40332cSJamie Gritton argv[argc] = NULL; 6593b40332cSJamie Gritton } 6603b40332cSJamie Gritton } 6613b40332cSJamie Gritton if (argv[0] == NULL) 6623b40332cSJamie Gritton return 0; 6633b40332cSJamie Gritton 6643b40332cSJamie Gritton if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) && 6653b40332cSJamie Gritton timeout != 0) { 6663b40332cSJamie Gritton clock_gettime(CLOCK_REALTIME, &j->timeout); 6673b40332cSJamie Gritton j->timeout.tv_sec += timeout; 6683b40332cSJamie Gritton } else 6693b40332cSJamie Gritton j->timeout.tv_sec = 0; 6703b40332cSJamie Gritton 6713b40332cSJamie Gritton injail = comparam == IP_EXEC_START || comparam == IP_COMMAND || 6723b40332cSJamie Gritton comparam == IP_EXEC_STOP; 673466df976SKyle Evans if (injail) 674466df976SKyle Evans setid = root_cpuset_id(); 675466df976SKyle Evans else 676466df976SKyle Evans setid = CPUSET_INVALID; 6773b40332cSJamie Gritton clean = bool_param(j->intparams[IP_EXEC_CLEAN]); 6783b40332cSJamie Gritton username = string_param(j->intparams[injail 6793b40332cSJamie Gritton ? IP_EXEC_JAIL_USER : IP_EXEC_SYSTEM_USER]); 6803b40332cSJamie Gritton sjuser = bool_param(j->intparams[IP_EXEC_SYSTEM_JAIL_USER]); 6813b40332cSJamie Gritton 6823b40332cSJamie Gritton consfd = 0; 6833b40332cSJamie Gritton if (injail && 6843b40332cSJamie Gritton (conslog = string_param(j->intparams[IP_EXEC_CONSOLELOG]))) { 68560080230SJamie Gritton if (check_path(j, "exec.consolelog", conslog, 1, NULL) < 0) 6863b40332cSJamie Gritton return -1; 6873b40332cSJamie Gritton consfd = 6883b40332cSJamie Gritton open(conslog, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE); 6893b40332cSJamie Gritton if (consfd < 0) { 6903b40332cSJamie Gritton jail_warnx(j, "open %s: %s", conslog, strerror(errno)); 6913b40332cSJamie Gritton return -1; 6923b40332cSJamie Gritton } 6933b40332cSJamie Gritton } 6943b40332cSJamie Gritton 6953b40332cSJamie Gritton comlen = 0; 6963b40332cSJamie Gritton for (i = 0; argv[i]; i++) 6973b40332cSJamie Gritton comlen += strlen(argv[i]) + 1; 6983b40332cSJamie Gritton j->comline = cs = emalloc(comlen); 6993b40332cSJamie Gritton for (i = 0; argv[i]; i++) { 7003b40332cSJamie Gritton strcpy(cs, argv[i]); 7013b40332cSJamie Gritton if (argv[i + 1]) { 7023b40332cSJamie Gritton cs += strlen(argv[i]) + 1; 7033b40332cSJamie Gritton cs[-1] = ' '; 7043b40332cSJamie Gritton } 7053b40332cSJamie Gritton } 7063b40332cSJamie Gritton if (verbose > 0) 7073b40332cSJamie Gritton jail_note(j, "run command%s%s%s: %s\n", 7083b40332cSJamie Gritton injail ? " in jail" : "", username ? " as " : "", 7093b40332cSJamie Gritton username ? username : "", j->comline); 7103b40332cSJamie Gritton 7113b40332cSJamie Gritton pid = fork(); 7123b40332cSJamie Gritton if (pid < 0) 7133b40332cSJamie Gritton err(1, "fork"); 7143b40332cSJamie Gritton if (pid > 0) { 715e5935495SJamie Gritton if (bg || !add_proc(j, pid)) { 7163b40332cSJamie Gritton free(j->comline); 7173b40332cSJamie Gritton j->comline = NULL; 71860080230SJamie Gritton return 0; 7193b40332cSJamie Gritton } else { 7203b40332cSJamie Gritton paralimit--; 7213b40332cSJamie Gritton return 1; 7223b40332cSJamie Gritton } 72360080230SJamie Gritton } 7243b40332cSJamie Gritton if (bg) 7253b40332cSJamie Gritton setsid(); 7263b40332cSJamie Gritton 7273b40332cSJamie Gritton /* Set up the environment and run the command */ 7283b40332cSJamie Gritton pwd = NULL; 7293b40332cSJamie Gritton lcap = NULL; 7303b40332cSJamie Gritton if ((clean || username) && injail && sjuser && 7313b40332cSJamie Gritton get_user_info(j, username, &pwd, &lcap) < 0) 7323b40332cSJamie Gritton exit(1); 7333b40332cSJamie Gritton if (injail) { 7343b40332cSJamie Gritton /* jail_attach won't chdir along with its chroot. */ 7353b40332cSJamie Gritton path = string_param(j->intparams[KP_PATH]); 7363b40332cSJamie Gritton if (path && chdir(path) < 0) { 7373b40332cSJamie Gritton jail_warnx(j, "chdir %s: %s", path, strerror(errno)); 7383b40332cSJamie Gritton exit(1); 7393b40332cSJamie Gritton } 7403b40332cSJamie Gritton if (int_param(j->intparams[IP_EXEC_FIB], &fib) && 7413b40332cSJamie Gritton setfib(fib) < 0) { 7423b40332cSJamie Gritton jail_warnx(j, "setfib: %s", strerror(errno)); 7433b40332cSJamie Gritton exit(1); 7443b40332cSJamie Gritton } 745466df976SKyle Evans 746466df976SKyle Evans /* 747466df976SKyle Evans * We wouldn't have specialized our affinity, so just setid to 748466df976SKyle Evans * root. We do this prior to attaching to avoid the kernel 749466df976SKyle Evans * having to create a transient cpuset that we'll promptly 750466df976SKyle Evans * free up with a reset to the jail's cpuset. 751466df976SKyle Evans * 752466df976SKyle Evans * This is just a best-effort to use as wide of mask as 753466df976SKyle Evans * possible. 754466df976SKyle Evans */ 755466df976SKyle Evans if (setid != CPUSET_INVALID) 756466df976SKyle Evans (void)cpuset_setid(CPU_WHICH_PID, -1, setid); 757466df976SKyle Evans 7583b40332cSJamie Gritton if (jail_attach(j->jid) < 0) { 7593b40332cSJamie Gritton jail_warnx(j, "jail_attach: %s", strerror(errno)); 7603b40332cSJamie Gritton exit(1); 7613b40332cSJamie Gritton } 7623b40332cSJamie Gritton } 7633b40332cSJamie Gritton if (clean || username) { 7643b40332cSJamie Gritton if (!(injail && sjuser) && 7653b40332cSJamie Gritton get_user_info(j, username, &pwd, &lcap) < 0) 7663b40332cSJamie Gritton exit(1); 7673b40332cSJamie Gritton if (clean) { 7683b40332cSJamie Gritton term = getenv("TERM"); 7693b40332cSJamie Gritton environ = &cleanenv; 7703b40332cSJamie Gritton setenv("PATH", "/bin:/usr/bin", 0); 7718632fa3eSJamie Gritton if (term != NULL) 7723b40332cSJamie Gritton setenv("TERM", term, 1); 7733b40332cSJamie Gritton } 774eb28afe4SJamie Gritton if (setgid(pwd->pw_gid) < 0) { 775eb28afe4SJamie Gritton jail_warnx(j, "setgid %d: %s", pwd->pw_gid, 776eb28afe4SJamie Gritton strerror(errno)); 777eb28afe4SJamie Gritton exit(1); 778eb28afe4SJamie Gritton } 7793b40332cSJamie Gritton if (setusercontext(lcap, pwd, pwd->pw_uid, username 7803b40332cSJamie Gritton ? LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN 7813b40332cSJamie Gritton : LOGIN_SETPATH | LOGIN_SETENV) < 0) { 7823b40332cSJamie Gritton jail_warnx(j, "setusercontext %s: %s", pwd->pw_name, 7833b40332cSJamie Gritton strerror(errno)); 7843b40332cSJamie Gritton exit(1); 7853b40332cSJamie Gritton } 7863b40332cSJamie Gritton login_close(lcap); 7873b40332cSJamie Gritton setenv("USER", pwd->pw_name, 1); 7883b40332cSJamie Gritton setenv("HOME", pwd->pw_dir, 1); 7893b40332cSJamie Gritton setenv("SHELL", 7903b40332cSJamie Gritton *pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL, 1); 791*5cf70549SJamie Gritton if (clean && username && chdir(pwd->pw_dir) < 0) { 7923b40332cSJamie Gritton jail_warnx(j, "chdir %s: %s", 7933b40332cSJamie Gritton pwd->pw_dir, strerror(errno)); 7943b40332cSJamie Gritton exit(1); 7953b40332cSJamie Gritton } 7963b40332cSJamie Gritton endpwent(); 7973b40332cSJamie Gritton } 7983b40332cSJamie Gritton 7993b40332cSJamie Gritton if (consfd != 0 && (dup2(consfd, 1) < 0 || dup2(consfd, 2) < 0)) { 8003b40332cSJamie Gritton jail_warnx(j, "exec.consolelog: %s", strerror(errno)); 8013b40332cSJamie Gritton exit(1); 8023b40332cSJamie Gritton } 8033b40332cSJamie Gritton closefrom(3); 804d031802bSJamie Gritton execvp(argv[0], __DECONST(char *const*, argv)); 8053b40332cSJamie Gritton jail_warnx(j, "exec %s: %s", argv[0], strerror(errno)); 8063b40332cSJamie Gritton exit(1); 8073b40332cSJamie Gritton } 8083b40332cSJamie Gritton 8093b40332cSJamie Gritton /* 810aa02af54SJamie Gritton * Add a process to the hash, tied to a jail. 811aa02af54SJamie Gritton */ 812e5935495SJamie Gritton static int 813aa02af54SJamie Gritton add_proc(struct cfjail *j, pid_t pid) 814aa02af54SJamie Gritton { 815aa02af54SJamie Gritton struct kevent ke; 816aa02af54SJamie Gritton struct cfjail *tj; 817aa02af54SJamie Gritton struct phash *ph; 818aa02af54SJamie Gritton 819aa02af54SJamie Gritton if (!kq && (kq = kqueue()) < 0) 820aa02af54SJamie Gritton err(1, "kqueue"); 821aa02af54SJamie Gritton EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); 822e5935495SJamie Gritton if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { 823e5935495SJamie Gritton if (errno == ESRCH) 824e5935495SJamie Gritton return 0; 825aa02af54SJamie Gritton err(1, "kevent"); 826e5935495SJamie Gritton } 827aa02af54SJamie Gritton ph = emalloc(sizeof(struct phash)); 828aa02af54SJamie Gritton ph->j = j; 829aa02af54SJamie Gritton ph->pid = pid; 830aa02af54SJamie Gritton LIST_INSERT_HEAD(&phash[pid % PHASH_SIZE], ph, le); 831aa02af54SJamie Gritton j->nprocs++; 832aa02af54SJamie Gritton j->flags |= JF_SLEEPQ; 833aa02af54SJamie Gritton if (j->timeout.tv_sec == 0) 834aa02af54SJamie Gritton requeue(j, &sleeping); 835aa02af54SJamie Gritton else { 836463a577bSEitan Adler /* File the jail in the sleep queue according to its timeout. */ 837aa02af54SJamie Gritton TAILQ_REMOVE(j->queue, j, tq); 838aa02af54SJamie Gritton TAILQ_FOREACH(tj, &sleeping, tq) { 839aa02af54SJamie Gritton if (!tj->timeout.tv_sec || 840aa02af54SJamie Gritton j->timeout.tv_sec < tj->timeout.tv_sec || 841aa02af54SJamie Gritton (j->timeout.tv_sec == tj->timeout.tv_sec && 842aa02af54SJamie Gritton j->timeout.tv_nsec <= tj->timeout.tv_nsec)) { 843aa02af54SJamie Gritton TAILQ_INSERT_BEFORE(tj, j, tq); 844aa02af54SJamie Gritton break; 845aa02af54SJamie Gritton } 846aa02af54SJamie Gritton } 847aa02af54SJamie Gritton if (tj == NULL) 848aa02af54SJamie Gritton TAILQ_INSERT_TAIL(&sleeping, j, tq); 849aa02af54SJamie Gritton j->queue = &sleeping; 850aa02af54SJamie Gritton } 851e5935495SJamie Gritton return 1; 852aa02af54SJamie Gritton } 853aa02af54SJamie Gritton 854aa02af54SJamie Gritton /* 855aa02af54SJamie Gritton * Remove any processes from the hash that correspond to a jail. 856aa02af54SJamie Gritton */ 857aa02af54SJamie Gritton static void 858aa02af54SJamie Gritton clear_procs(struct cfjail *j) 859aa02af54SJamie Gritton { 860aa02af54SJamie Gritton struct kevent ke; 861aa02af54SJamie Gritton struct phash *ph, *tph; 862aa02af54SJamie Gritton int i; 863aa02af54SJamie Gritton 864aa02af54SJamie Gritton j->nprocs = 0; 865aa02af54SJamie Gritton for (i = 0; i < PHASH_SIZE; i++) 866aa02af54SJamie Gritton LIST_FOREACH_SAFE(ph, &phash[i], le, tph) 867aa02af54SJamie Gritton if (ph->j == j) { 868aa02af54SJamie Gritton EV_SET(&ke, ph->pid, EVFILT_PROC, EV_DELETE, 869aa02af54SJamie Gritton NOTE_EXIT, 0, NULL); 870aa02af54SJamie Gritton (void)kevent(kq, &ke, 1, NULL, 0, NULL); 871aa02af54SJamie Gritton LIST_REMOVE(ph, le); 872aa02af54SJamie Gritton free(ph); 873aa02af54SJamie Gritton } 874aa02af54SJamie Gritton } 875aa02af54SJamie Gritton 876aa02af54SJamie Gritton /* 877aa02af54SJamie Gritton * Find the jail that corresponds to an exited process. 878aa02af54SJamie Gritton */ 879aa02af54SJamie Gritton static struct cfjail * 880aa02af54SJamie Gritton find_proc(pid_t pid) 881aa02af54SJamie Gritton { 882aa02af54SJamie Gritton struct cfjail *j; 883aa02af54SJamie Gritton struct phash *ph; 884aa02af54SJamie Gritton 885aa02af54SJamie Gritton LIST_FOREACH(ph, &phash[pid % PHASH_SIZE], le) 886aa02af54SJamie Gritton if (ph->pid == pid) { 887aa02af54SJamie Gritton j = ph->j; 888aa02af54SJamie Gritton LIST_REMOVE(ph, le); 889aa02af54SJamie Gritton free(ph); 890aa02af54SJamie Gritton return --j->nprocs ? NULL : j; 891aa02af54SJamie Gritton } 892aa02af54SJamie Gritton return NULL; 893aa02af54SJamie Gritton } 894aa02af54SJamie Gritton 895aa02af54SJamie Gritton /* 8962671ee73SJamie Gritton * Send SIGTERM to all processes in a jail and wait for them to die. 8972671ee73SJamie Gritton */ 898aa02af54SJamie Gritton static int 8992671ee73SJamie Gritton term_procs(struct cfjail *j) 9002671ee73SJamie Gritton { 9012671ee73SJamie Gritton struct kinfo_proc *ki; 9022671ee73SJamie Gritton int i, noted, pcnt, timeout; 9032671ee73SJamie Gritton 9042671ee73SJamie Gritton static kvm_t *kd; 9052671ee73SJamie Gritton 9062671ee73SJamie Gritton if (!int_param(j->intparams[IP_STOP_TIMEOUT], &timeout)) 9072671ee73SJamie Gritton timeout = DEFAULT_STOP_TIMEOUT; 9082671ee73SJamie Gritton else if (timeout == 0) 9092671ee73SJamie Gritton return 0; 9102671ee73SJamie Gritton 9112671ee73SJamie Gritton if (kd == NULL) { 9121ca35de4SJamie Gritton kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); 9132671ee73SJamie Gritton if (kd == NULL) 9141ca35de4SJamie Gritton return 0; 9152671ee73SJamie Gritton } 9162671ee73SJamie Gritton 9172671ee73SJamie Gritton ki = kvm_getprocs(kd, KERN_PROC_PROC, 0, &pcnt); 9182671ee73SJamie Gritton if (ki == NULL) 9191ca35de4SJamie Gritton return 0; 9202671ee73SJamie Gritton noted = 0; 9212671ee73SJamie Gritton for (i = 0; i < pcnt; i++) 9222671ee73SJamie Gritton if (ki[i].ki_jid == j->jid && 9232671ee73SJamie Gritton kill(ki[i].ki_pid, SIGTERM) == 0) { 924e5935495SJamie Gritton (void)add_proc(j, ki[i].ki_pid); 9252671ee73SJamie Gritton if (verbose > 0) { 9262671ee73SJamie Gritton if (!noted) { 9272671ee73SJamie Gritton noted = 1; 9282671ee73SJamie Gritton jail_note(j, "sent SIGTERM to:"); 9292671ee73SJamie Gritton } 9302671ee73SJamie Gritton printf(" %d", ki[i].ki_pid); 9312671ee73SJamie Gritton } 9322671ee73SJamie Gritton } 9332671ee73SJamie Gritton if (noted) 9342671ee73SJamie Gritton printf("\n"); 9352671ee73SJamie Gritton if (j->nprocs > 0) { 9362671ee73SJamie Gritton clock_gettime(CLOCK_REALTIME, &j->timeout); 9372671ee73SJamie Gritton j->timeout.tv_sec += timeout; 9382671ee73SJamie Gritton return 1; 9392671ee73SJamie Gritton } 9402671ee73SJamie Gritton return 0; 9412671ee73SJamie Gritton } 9422671ee73SJamie Gritton 9432671ee73SJamie Gritton /* 9442671ee73SJamie Gritton * Look up a user in the passwd and login.conf files. 9452671ee73SJamie Gritton */ 9462671ee73SJamie Gritton static int 9472671ee73SJamie Gritton get_user_info(struct cfjail *j, const char *username, 9482671ee73SJamie Gritton const struct passwd **pwdp, login_cap_t **lcapp) 9492671ee73SJamie Gritton { 9502671ee73SJamie Gritton const struct passwd *pwd; 9512671ee73SJamie Gritton 952fcc43d06SJamie Gritton errno = 0; 9532671ee73SJamie Gritton *pwdp = pwd = username ? getpwnam(username) : getpwuid(getuid()); 9542671ee73SJamie Gritton if (pwd == NULL) { 9552671ee73SJamie Gritton if (errno) 9562671ee73SJamie Gritton jail_warnx(j, "getpwnam%s%s: %s", username ? " " : "", 9572671ee73SJamie Gritton username ? username : "", strerror(errno)); 9582671ee73SJamie Gritton else if (username) 9592671ee73SJamie Gritton jail_warnx(j, "%s: no such user", username); 9602671ee73SJamie Gritton else 9612671ee73SJamie Gritton jail_warnx(j, "unknown uid %d", getuid()); 9622671ee73SJamie Gritton return -1; 9632671ee73SJamie Gritton } 9642671ee73SJamie Gritton *lcapp = login_getpwclass(pwd); 9652671ee73SJamie Gritton if (*lcapp == NULL) { 9662671ee73SJamie Gritton jail_warnx(j, "getpwclass %s: %s", pwd->pw_name, 9672671ee73SJamie Gritton strerror(errno)); 9682671ee73SJamie Gritton return -1; 9692671ee73SJamie Gritton } 9702671ee73SJamie Gritton /* Set the groups while the group file is still available */ 9712671ee73SJamie Gritton if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) { 9722671ee73SJamie Gritton jail_warnx(j, "initgroups %s: %s", pwd->pw_name, 9732671ee73SJamie Gritton strerror(errno)); 9742671ee73SJamie Gritton return -1; 9752671ee73SJamie Gritton } 9762671ee73SJamie Gritton return 0; 9772671ee73SJamie Gritton } 9788ebbf0e2SJamie Gritton 9798ebbf0e2SJamie Gritton /* 9808ebbf0e2SJamie Gritton * Make sure a mount or consolelog path is a valid absolute pathname 9818ebbf0e2SJamie Gritton * with no symlinks. 9828ebbf0e2SJamie Gritton */ 9838ebbf0e2SJamie Gritton static int 9845264032fSJamie Gritton check_path(struct cfjail *j, const char *pname, const char *path, int isfile, 9855264032fSJamie Gritton const char *umount_type) 9868ebbf0e2SJamie Gritton { 9875264032fSJamie Gritton struct stat st, mpst; 9885264032fSJamie Gritton struct statfs stfs; 9898ebbf0e2SJamie Gritton char *tpath, *p; 9908ebbf0e2SJamie Gritton const char *jailpath; 9918ebbf0e2SJamie Gritton size_t jplen; 9928ebbf0e2SJamie Gritton 9938ebbf0e2SJamie Gritton if (path[0] != '/') { 9948ebbf0e2SJamie Gritton jail_warnx(j, "%s: %s: not an absolute pathname", 9958ebbf0e2SJamie Gritton pname, path); 9968ebbf0e2SJamie Gritton return -1; 9978ebbf0e2SJamie Gritton } 9988ebbf0e2SJamie Gritton /* 9998ebbf0e2SJamie Gritton * Only check for symlinks in components below the jail's path, 10008ebbf0e2SJamie Gritton * since that's where the security risk lies. 10018ebbf0e2SJamie Gritton */ 10028ebbf0e2SJamie Gritton jailpath = string_param(j->intparams[KP_PATH]); 10038ebbf0e2SJamie Gritton if (jailpath == NULL) 10048ebbf0e2SJamie Gritton jailpath = ""; 10058ebbf0e2SJamie Gritton jplen = strlen(jailpath); 10065264032fSJamie Gritton if (!strncmp(path, jailpath, jplen) && path[jplen] == '/') { 10078ebbf0e2SJamie Gritton tpath = alloca(strlen(path) + 1); 10088ebbf0e2SJamie Gritton strcpy(tpath, path); 10098ebbf0e2SJamie Gritton for (p = tpath + jplen; p != NULL; ) { 10108ebbf0e2SJamie Gritton p = strchr(p + 1, '/'); 10118ebbf0e2SJamie Gritton if (p) 10128ebbf0e2SJamie Gritton *p = '\0'; 10138ebbf0e2SJamie Gritton if (lstat(tpath, &st) < 0) { 10148ebbf0e2SJamie Gritton if (errno == ENOENT && isfile && !p) 10158ebbf0e2SJamie Gritton break; 10168ebbf0e2SJamie Gritton jail_warnx(j, "%s: %s: %s", pname, tpath, 10178ebbf0e2SJamie Gritton strerror(errno)); 10188ebbf0e2SJamie Gritton return -1; 10198ebbf0e2SJamie Gritton } 10208ebbf0e2SJamie Gritton if (S_ISLNK(st.st_mode)) { 10218ebbf0e2SJamie Gritton jail_warnx(j, "%s: %s is a symbolic link", 10228ebbf0e2SJamie Gritton pname, tpath); 10238ebbf0e2SJamie Gritton return -1; 10248ebbf0e2SJamie Gritton } 10258ebbf0e2SJamie Gritton if (p) 10268ebbf0e2SJamie Gritton *p = '/'; 10278ebbf0e2SJamie Gritton } 10285264032fSJamie Gritton } 10295264032fSJamie Gritton if (umount_type != NULL) { 10305264032fSJamie Gritton if (stat(path, &st) < 0 || statfs(path, &stfs) < 0) { 10315264032fSJamie Gritton jail_warnx(j, "%s: %s: %s", pname, path, 10325264032fSJamie Gritton strerror(errno)); 10335264032fSJamie Gritton return -1; 10345264032fSJamie Gritton } 10355264032fSJamie Gritton if (stat(stfs.f_mntonname, &mpst) < 0) { 10365264032fSJamie Gritton jail_warnx(j, "%s: %s: %s", pname, stfs.f_mntonname, 10375264032fSJamie Gritton strerror(errno)); 10385264032fSJamie Gritton return -1; 10395264032fSJamie Gritton } 10405264032fSJamie Gritton if (st.st_ino != mpst.st_ino) { 10415264032fSJamie Gritton jail_warnx(j, "%s: %s: not a mount point", 10425264032fSJamie Gritton pname, path); 10435264032fSJamie Gritton return -1; 10445264032fSJamie Gritton } 10455264032fSJamie Gritton if (strcmp(stfs.f_fstypename, umount_type)) { 10465264032fSJamie Gritton jail_warnx(j, "%s: %s: not a %s mount", 10475264032fSJamie Gritton pname, path, umount_type); 10485264032fSJamie Gritton return -1; 10495264032fSJamie Gritton } 10505264032fSJamie Gritton } 10518ebbf0e2SJamie Gritton return 0; 10528ebbf0e2SJamie Gritton } 1053