Index: stable/9/lib/libc/sys/jail.2 =================================================================== --- stable/9/lib/libc/sys/jail.2 (revision 235838) +++ stable/9/lib/libc/sys/jail.2 (revision 235839) @@ -1,450 +1,412 @@ .\" Copyright (c) 1999 Poul-Henning Kamp. .\" Copyright (c) 2009 James Gritton. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd February 8, 2012 .Dt JAIL 2 .Os .Sh NAME .Nm jail , .Nm jail_get , .Nm jail_set , .Nm jail_remove , .Nm jail_attach .Nd create and manage system jails .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In sys/param.h .In sys/jail.h .Ft int .Fn jail "struct jail *jail" .Ft int .Fn jail_attach "int jid" .Ft int .Fn jail_remove "int jid" .In sys/uio.h .Ft int .Fn jail_get "struct iovec *iov" "u_int niov" "int flags" .Ft int .Fn jail_set "struct iovec *iov" "u_int niov" "int flags" .Sh DESCRIPTION The .Fn jail system call sets up a jail and locks the current process in it. .Pp The argument is a pointer to a structure describing the prison: .Bd -literal -offset indent struct jail { - u_int32_t version; + uint32_t version; char *path; char *hostname; char *jailname; unsigned int ip4s; unsigned int ip6s; struct in_addr *ip4; struct in6_addr *ip6; }; .Ed .Pp .Dq Li version defines the version of the API in use. .Dv JAIL_API_VERSION is defined for the current version. .Pp The .Dq Li path pointer should be set to the directory which is to be the root of the prison. .Pp The .Dq Li hostname pointer can be set to the hostname of the prison. This can be changed from the inside of the prison. .Pp The .Dq Li jailname pointer is an optional name that can be assigned to the jail for example for management purposes. .Pp The .Dq Li ip4s and .Dq Li ip6s give the numbers of IPv4 and IPv6 addresses that will be passed via their respective pointers. .Pp The .Dq Li ip4 and .Dq Li ip6 pointers can be set to an arrays of IPv4 and IPv6 addresses to be assigned to the prison, or NULL if none. IPv4 addresses must be in network byte order. .Pp This is equivalent to the .Fn jail_set system call (see below), with the parameters .Va path , .Va host.hostname , .Va name , .Va ip4.addr , and .Va ip6.addr , and with the .Dv JAIL_ATTACH flag. .Pp The .Fn jail_set system call creates a new jail, or modifies an existing one, and optionally locks the current process in it. Jail parameters are passed as an array of name-value pairs in the array .Fa iov , containing .Fa niov elements. Parameter names are a null-terminated string, and values may be strings, integers, or other arbitrary data. Some parameters are boolean, and do not have a value (their length is zero) but are set by the name alone with or without a .Dq no prefix, e.g. .Va persist or .Va nopersist . Any parameters not set will be given default values, generally based on the current environment. .Pp Jails have a set of core parameters, and modules can add their own jail parameters. The current set of available parameters, and their formats, can be retrieved via the .Va security.jail.param sysctl MIB entry. Notable parameters include those mentioned in the .Fn jail description above, as well as .Va jid and .Va name , which identify the jail being created or modified. See .Xr jail 8 for more information on the core jail parameters. .Pp The .Fa flags arguments consists of one or more of the following flags: .Bl -tag -width indent .It Dv JAIL_CREATE Create a new jail. If a .Va jid or .Va name parameters exists, they must not refer to an existing jail. .It Dv JAIL_UPDATE Modify an existing jail. One of the .Va jid or .Va name parameters must exist, and must refer to an existing jail. If both .Dv JAIL_CREATE and .Dv JAIL_UPDATE are set, a jail will be created if it does not yet exist, and modified if it does exist. .It Dv JAIL_ATTACH In addition to creating or modifying the jail, attach the current process to it, as with the .Fn jail_attach system call. .It Dv JAIL_DYING Allow setting a jail that is in the process of being removed. .El .Pp The .Fn jail_get system call retrieves jail parameters, using the same name-value list as .Fn jail_set in the .Fa iov and .Fa niov arguments. The jail to read can be specified by either .Va jid or .Va name by including those parameters in the list. If they are included but are not intended to be the search key, they should be cleared (zero and the empty string respectively). .Pp The special parameter .Va lastjid can be used to retrieve a list of all jails. It will fetch the jail with the jid above and closest to the passed value. The first jail (usually but not always jid 1) can be found by passing a .Va lastjid of zero. .Pp The .Fa flags arguments consists of one or more following flags: .Bl -tag -width indent .It Dv JAIL_DYING Allow getting a jail that is in the process of being removed. .El .Pp The .Fn jail_attach system call attaches the current process to an existing jail, identified by .Fa jid . .Pp The .Fn jail_remove system call removes the jail identified by .Fa jid . It will kill all processes belonging to the jail, and remove any children of that jail. .Sh RETURN VALUES If successful, .Fn jail , .Fn jail_set , and .Fn jail_get return a non-negative integer, termed the jail identifier (JID). They return \-1 on failure, and set .Va errno to indicate the error. .Pp .Rv -std jail_attach jail_remove -.Sh PRISON? -Once a process has been put in a prison, it and its descendants cannot escape -the prison. -.Pp -Inside the prison, the concept of -.Dq superuser -is very diluted. -In general, -it can be assumed that nothing can be mangled from inside a prison which -does not exist entirely inside that prison. -For instance the directory -tree below -.Dq Li path -can be manipulated all the ways a root can normally do it, including -.Dq Li "rm -rf /*" -but new device special nodes cannot be created because they reference -shared resources (the device drivers in the kernel). -The effective -.Dq securelevel -for a process is the greater of the global -.Dq securelevel -or, if present, the per-jail -.Dq securelevel . -.Pp -All IP activity will be forced to happen to/from the IP number specified, -which should be an alias on one of the network interfaces. -All connections to/from the loopback address -.Pf ( Li 127.0.0.1 -for IPv4, -.Li ::1 -for IPv6) will be changed to be to/from the primary address -of the jail for the given address family. -.Pp -It is possible to identify a process as jailed by examining -.Dq Li /proc//status : -it will show a field near the end of the line, either as -a single hyphen for a process at large, or the name currently -set for the prison for jailed processes. .Sh ERRORS The .Fn jail system call will fail if: .Bl -tag -width Er .It Bq Er EPERM This process is not allowed to create a jail, either because it is not the super-user, or because it would exceed the jail's .Va children.max limit. .It Bq Er EFAULT .Fa jail points to an address outside the allocated address space of the process. .It Bq Er EINVAL The version number of the argument is not correct. .It Bq Er EAGAIN No free JID could be found. .El .Pp The .Fn jail_set system call will fail if: .Bl -tag -width Er .It Bq Er EPERM This process is not allowed to create a jail, either because it is not the super-user, or because it would exceed the jail's .Va children.max limit. .It Bq Er EPERM A jail parameter was set to a less restrictive value then the current environment. .It Bq Er EFAULT .Fa Iov , or one of the addresses contained within it, points to an address outside the allocated address space of the process. .It Bq Er ENOENT The jail referred to by a .Va jid or .Va name parameter does not exist, and the .Dv JAIL_CREATE flag is not set. .It Bq Er ENOENT The jail referred to by a .Va jid is not accessible by the process, because the process is in a different -jail. +jail. .It Bq Er EEXIST The jail referred to by a .Va jid or .Va name parameter exists, and the .Dv JAIL_UPDATE flag is not set. .It Bq Er EINVAL A supplied parameter is the wrong size. .It Bq Er EINVAL A supplied parameter is out of range. .It Bq Er EINVAL A supplied string parameter is not null-terminated. .It Bq Er EINVAL A supplied parameter name does not match any known parameters. .It Bq Er EINVAL One of the .Dv JAIL_CREATE or .Dv JAIL_UPDATE flags is not set. .It Bq Er ENAMETOOLONG A supplied string parameter is longer than allowed. .It Bq Er EAGAIN There are no jail IDs left. .El .Pp The .Fn jail_get system call will fail if: .Bl -tag -width Er .It Bq Er EFAULT .Fa Iov , or one of the addresses contained within it, points to an address outside the allocated address space of the process. .It Bq Er ENOENT The jail referred to by a .Va jid or .Va name parameter does not exist. .It Bq Er ENOENT The jail referred to by a .Va jid is not accessible by the process, because the process is in a different -jail. +jail. .It Bq Er ENOENT The .Va lastjid parameter is greater than the highest current jail ID. .It Bq Er EINVAL A supplied parameter is the wrong size. .It Bq Er EINVAL A supplied parameter name does not match any known parameters. .El .Pp The .Fn jail_attach and .Fn jail_remove system calls will fail if: .Bl -tag -width Er .It Bq Er EPERM A user other than the super-user attempted to attach to or remove a jail. .It Bq Er EINVAL The jail specified by .Fa jid does not exist. .El .Pp Further .Fn jail , .Fn jail_set , and .Fn jail_attach call .Xr chroot 2 -internally, so it can fail for all the same reasons. +internally, so they can fail for all the same reasons. Please consult the .Xr chroot 2 manual page for details. .Sh SEE ALSO .Xr chdir 2 , .Xr chroot 2 , .Xr jail 8 .Sh HISTORY The .Fn jail system call appeared in .Fx 4.0 . The .Fn jail_attach system call appeared in .Fx 5.1 . The .Fn jail_set , .Fn jail_get , and .Fn jail_remove system calls appeared in .Fx 8.0 . .Sh AUTHORS The jail feature was written by .An Poul-Henning Kamp for R&D Associates .Dq Li http://www.rndassociates.com/ who contributed it to .Fx . .An James Gritton added the extensible jail parameters and hierarchical jails. Index: stable/9/lib/libc/sys =================================================================== --- stable/9/lib/libc/sys (revision 235838) +++ stable/9/lib/libc/sys (revision 235839) Property changes on: stable/9/lib/libc/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/lib/libc/sys:r234712 Index: stable/9/lib/libc =================================================================== --- stable/9/lib/libc (revision 235838) +++ stable/9/lib/libc (revision 235839) Property changes on: stable/9/lib/libc ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /head/lib/libc:r234712 Merged /projects/jailconf/lib/libc:r214121 Index: stable/9/usr.sbin/jail/Makefile =================================================================== --- stable/9/usr.sbin/jail/Makefile (revision 235838) +++ stable/9/usr.sbin/jail/Makefile (revision 235839) @@ -1,17 +1,24 @@ # $FreeBSD$ .include PROG= jail -MAN= jail.8 -DPADD= ${LIBJAIL} ${LIBUTIL} -LDADD= -ljail -lutil +MAN= jail.8 jail.conf.5 +SRCS= jail.c command.c config.c state.c jailp.h jaillex.l jailparse.y y.tab.h +DPADD= ${LIBJAIL} ${LIBKVM} ${LIBUTIL} ${LIBL} +LDADD= -ljail -lkvm -lutil -ll + +YFLAGS+=-v +CFLAGS+=-I. -I${.CURDIR} + .if ${MK_INET6_SUPPORT} != "no" CFLAGS+= -DINET6 .endif .if ${MK_INET_SUPPORT} != "no" CFLAGS+= -DINET .endif + +CLEANFILES= y.output .include Index: stable/9/usr.sbin/jail/command.c =================================================================== --- stable/9/usr.sbin/jail/command.c (nonexistent) +++ stable/9/usr.sbin/jail/command.c (revision 235839) @@ -0,0 +1,857 @@ +/*- + * Copyright (c) 2011 James Gritton + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jailp.h" + +#define DEFAULT_STOP_TIMEOUT 10 +#define PHASH_SIZE 256 + +LIST_HEAD(phhead, phash); + +struct phash { + LIST_ENTRY(phash) le; + struct cfjail *j; + pid_t pid; +}; + +int paralimit = -1; + +extern char **environ; + +static int run_command(struct cfjail *j); +static void add_proc(struct cfjail *j, pid_t pid); +static void clear_procs(struct cfjail *j); +static struct cfjail *find_proc(pid_t pid); +static int term_procs(struct cfjail *j); +static int get_user_info(struct cfjail *j, const char *username, + const struct passwd **pwdp, login_cap_t **lcapp); +static int check_path(struct cfjail *j, const char *pname, const char *path, + int isfile, const char *umount_type); + +static struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping); +static struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable); +static struct cfstring dummystring = { .len = 1 }; +static struct phhead phash[PHASH_SIZE]; +static int kq; + +/* + * Run the next command associated with a jail. + */ +int +next_command(struct cfjail *j) +{ + enum intparam comparam; + int create_failed; + + if (paralimit == 0) { + requeue(j, &runnable); + return 1; + } + create_failed = (j->flags & (JF_STOP | JF_FAILED)) == JF_FAILED; + comparam = *j->comparam; + for (;;) { + if (j->comstring == NULL) { + j->comparam += create_failed ? -1 : 1; + switch ((comparam = *j->comparam)) { + case IP__NULL: + return 0; + case IP_MOUNT_DEVFS: + if (!bool_param(j->intparams[IP_MOUNT_DEVFS])) + continue; + /* FALLTHROUGH */ + case IP__OP: + case IP_STOP_TIMEOUT: + j->comstring = &dummystring; + break; + default: + if (j->intparams[comparam] == NULL) + continue; + j->comstring = create_failed + ? TAILQ_LAST(&j->intparams[comparam]->val, + cfstrings) + : TAILQ_FIRST(&j->intparams[comparam]->val); + } + } else { + j->comstring = j->comstring == &dummystring ? NULL : + create_failed + ? TAILQ_PREV(j->comstring, cfstrings, tq) + : TAILQ_NEXT(j->comstring, tq); + } + if (j->comstring == NULL || j->comstring->len == 0 || + (create_failed && (comparam == IP_EXEC_PRESTART || + comparam == IP_EXEC_START || comparam == IP_COMMAND || + comparam == IP_EXEC_POSTSTART))) + continue; + switch (run_command(j)) { + case -1: + failed(j); + /* FALLTHROUGH */ + case 1: + return 1; + } + } +} + +/* + * Check command exit status + */ +int +finish_command(struct cfjail *j) +{ + int error; + + if (!(j->flags & JF_SLEEPQ)) + return 0; + j->flags &= ~JF_SLEEPQ; + if (*j->comparam == IP_STOP_TIMEOUT) + { + j->flags &= ~JF_TIMEOUT; + j->pstatus = 0; + return 0; + } + paralimit++; + if (!TAILQ_EMPTY(&runnable)) + requeue(TAILQ_FIRST(&runnable), &ready); + error = 0; + if (j->flags & JF_TIMEOUT) { + j->flags &= ~JF_TIMEOUT; + if (*j->comparam != IP_STOP_TIMEOUT) { + jail_warnx(j, "%s: timed out", j->comline); + failed(j); + error = -1; + } else if (verbose > 0) + jail_note(j, "timed out\n"); + } else if (j->pstatus != 0) { + if (WIFSIGNALED(j->pstatus)) + jail_warnx(j, "%s: exited on signal %d", + j->comline, WTERMSIG(j->pstatus)); + else + jail_warnx(j, "%s: failed", j->comline); + j->pstatus = 0; + failed(j); + error = -1; + } + free(j->comline); + j->comline = NULL; + return error; +} + +/* + * Check for finished processes or timeouts. + */ +struct cfjail * +next_proc(int nonblock) +{ + struct kevent ke; + struct timespec ts; + struct timespec *tsp; + struct cfjail *j; + + if (!TAILQ_EMPTY(&sleeping)) { + again: + tsp = NULL; + if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) { + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec = j->timeout.tv_sec - ts.tv_sec; + ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec; + if (ts.tv_nsec < 0) { + ts.tv_sec--; + ts.tv_nsec += 1000000000; + } + if (ts.tv_sec < 0 || + (ts.tv_sec == 0 && ts.tv_nsec == 0)) { + j->flags |= JF_TIMEOUT; + clear_procs(j); + return j; + } + tsp = &ts; + } + if (nonblock) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + tsp = &ts; + } + switch (kevent(kq, NULL, 0, &ke, 1, tsp)) { + case -1: + if (errno != EINTR) + err(1, "kevent"); + goto again; + case 0: + if (!nonblock) { + j = TAILQ_FIRST(&sleeping); + j->flags |= JF_TIMEOUT; + clear_procs(j); + return j; + } + break; + case 1: + (void)waitpid(ke.ident, NULL, WNOHANG); + if ((j = find_proc(ke.ident))) { + j->pstatus = ke.data; + return j; + } + goto again; + } + } + return NULL; +} + +/* + * Run a single command for a jail, possible inside the jail. + */ +int +run_command(struct cfjail *j) +{ + const struct passwd *pwd; + const struct cfstring *comstring, *s; + login_cap_t *lcap; + char **argv; + char *cs, *comcs, *devpath; + const char *jidstr, *conslog, *path, *ruleset, *term, *username; + enum intparam comparam; + size_t comlen; + pid_t pid; + int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout; +#if defined(INET) || defined(INET6) + char *addr; +#endif + + static char *cleanenv; + + /* Perform some operations that aren't actually commands */ + comparam = *j->comparam; + down = j->flags & (JF_STOP | JF_FAILED); + switch (comparam) { + case IP_STOP_TIMEOUT: + return term_procs(j); + + case IP__OP: + if (down) { + if (jail_remove(j->jid) < 0 && errno == EPERM) { + jail_warnx(j, "jail_remove: %s", + strerror(errno)); + return -1; + } + if (verbose > 0 || (verbose == 0 && (j->flags & JF_STOP + ? note_remove : j->name != NULL))) + jail_note(j, "removed\n"); + j->jid = -1; + if (j->flags & JF_STOP) + dep_done(j, DF_LIGHT); + else + j->flags &= ~JF_PERSIST; + } else { + if (create_jail(j) < 0) + return -1; + if (verbose >= 0 && (j->name || verbose > 0)) + jail_note(j, "created\n"); + dep_done(j, DF_LIGHT); + } + return 0; + + default: ; + } + /* + * Collect exec arguments. Internal commands for network and + * mounting build their own argument lists. + */ + comstring = j->comstring; + bg = 0; + switch (comparam) { +#ifdef INET + case IP__IP4_IFADDR: + argv = alloca(8 * sizeof(char *)); + *(const char **)&argv[0] = _PATH_IFCONFIG; + if ((cs = strchr(comstring->s, '|'))) { + argv[1] = alloca(cs - comstring->s + 1); + strlcpy(argv[1], comstring->s, cs - comstring->s + 1); + addr = cs + 1; + } else { + *(const char **)&argv[1] = + string_param(j->intparams[IP_INTERFACE]); + addr = comstring->s; + } + *(const char **)&argv[2] = "inet"; + if (!(cs = strchr(addr, '/'))) { + argv[3] = addr; + *(const char **)&argv[4] = "netmask"; + *(const char **)&argv[5] = "255.255.255.255"; + argc = 6; + } else if (strchr(cs + 1, '.')) { + argv[3] = alloca(cs - addr + 1); + strlcpy(argv[3], addr, cs - addr + 1); + *(const char **)&argv[4] = "netmask"; + *(const char **)&argv[5] = cs + 1; + argc = 6; + } else { + argv[3] = addr; + argc = 4; + } + *(const char **)&argv[argc] = down ? "-alias" : "alias"; + argv[argc + 1] = NULL; + break; +#endif + +#ifdef INET6 + case IP__IP6_IFADDR: + argv = alloca(8 * sizeof(char *)); + *(const char **)&argv[0] = _PATH_IFCONFIG; + if ((cs = strchr(comstring->s, '|'))) { + argv[1] = alloca(cs - comstring->s + 1); + strlcpy(argv[1], comstring->s, cs - comstring->s + 1); + addr = cs + 1; + } else { + *(const char **)&argv[1] = + string_param(j->intparams[IP_INTERFACE]); + addr = comstring->s; + } + *(const char **)&argv[2] = "inet6"; + argv[3] = addr; + if (!(cs = strchr(addr, '/'))) { + *(const char **)&argv[4] = "prefixlen"; + *(const char **)&argv[5] = "128"; + argc = 6; + } else + argc = 4; + *(const char **)&argv[argc] = down ? "-alias" : "alias"; + argv[argc + 1] = NULL; + break; +#endif + + case IP_VNET_INTERFACE: + argv = alloca(5 * sizeof(char *)); + *(const char **)&argv[0] = _PATH_IFCONFIG; + argv[1] = comstring->s; + *(const char **)&argv[2] = down ? "-vnet" : "vnet"; + jidstr = string_param(j->intparams[KP_JID]); + *(const char **)&argv[3] = + jidstr ? jidstr : string_param(j->intparams[KP_NAME]); + argv[4] = NULL; + break; + + case IP_MOUNT: + case IP__MOUNT_FROM_FSTAB: + argv = alloca(8 * sizeof(char *)); + comcs = alloca(comstring->len + 1); + strcpy(comcs, comstring->s); + argc = 0; + for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4; + cs = strtok(NULL, " \t\f\v\r\n")) + argv[argc++] = cs; + if (argc == 0) + return 0; + if (argc < 3) { + jail_warnx(j, "%s: %s: missing information", + j->intparams[comparam]->name, comstring->s); + return -1; + } + if (check_path(j, j->intparams[comparam]->name, argv[1], 0, + down ? argv[2] : NULL) < 0) + return -1; + if (down) { + argv[4] = NULL; + argv[3] = argv[1]; + *(const char **)&argv[0] = "/sbin/umount"; + } else { + if (argc == 4) { + argv[7] = NULL; + argv[6] = argv[1]; + argv[5] = argv[0]; + argv[4] = argv[3]; + *(const char **)&argv[3] = "-o"; + } else { + argv[5] = NULL; + argv[4] = argv[1]; + argv[3] = argv[0]; + } + *(const char **)&argv[0] = _PATH_MOUNT; + } + *(const char **)&argv[1] = "-t"; + break; + + case IP_MOUNT_DEVFS: + argv = alloca(7 * sizeof(char *)); + path = string_param(j->intparams[KP_PATH]); + if (path == NULL) { + jail_warnx(j, "mount.devfs: no path"); + return -1; + } + devpath = alloca(strlen(path) + 5); + sprintf(devpath, "%s/dev", path); + if (check_path(j, "mount.devfs", devpath, 0, + down ? "devfs" : NULL) < 0) + return -1; + if (down) { + *(const char **)&argv[0] = "/sbin/umount"; + argv[1] = devpath; + argv[2] = NULL; + } else { + *(const char **)&argv[0] = _PATH_MOUNT; + *(const char **)&argv[1] = "-t"; + *(const char **)&argv[2] = "devfs"; + ruleset = string_param(j->intparams[KP_DEVFS_RULESET]); + if (!ruleset) + ruleset = "4"; /* devfsrules_jail */ + argv[3] = alloca(11 + strlen(ruleset)); + sprintf(argv[3], "-oruleset=%s", ruleset); + *(const char **)&argv[4] = "."; + argv[5] = devpath; + argv[6] = NULL; + } + break; + + case IP_COMMAND: + if (j->name != NULL) + goto default_command; + argc = 0; + TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq) + argc++; + argv = alloca((argc + 1) * sizeof(char *)); + argc = 0; + TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq) + argv[argc++] = s->s; + argv[argc] = NULL; + j->comstring = &dummystring; + break; + + default: + default_command: + if ((cs = strpbrk(comstring->s, "!\"$&'()*;<>?[\\]`{|}~")) && + !(cs[0] == '&' && cs[1] == '\0')) { + argv = alloca(4 * sizeof(char *)); + *(const char **)&argv[0] = _PATH_BSHELL; + *(const char **)&argv[1] = "-c"; + argv[2] = comstring->s; + argv[3] = NULL; + } else { + if (cs) { + *cs = 0; + bg = 1; + } + comcs = alloca(comstring->len + 1); + strcpy(comcs, comstring->s); + argc = 0; + for (cs = strtok(comcs, " \t\f\v\r\n"); cs; + cs = strtok(NULL, " \t\f\v\r\n")) + argc++; + argv = alloca((argc + 1) * sizeof(char *)); + strcpy(comcs, comstring->s); + argc = 0; + for (cs = strtok(comcs, " \t\f\v\r\n"); cs; + cs = strtok(NULL, " \t\f\v\r\n")) + argv[argc++] = cs; + argv[argc] = NULL; + } + } + if (argv[0] == NULL) + return 0; + + if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) && + timeout != 0) { + clock_gettime(CLOCK_REALTIME, &j->timeout); + j->timeout.tv_sec += timeout; + } else + j->timeout.tv_sec = 0; + + injail = comparam == IP_EXEC_START || comparam == IP_COMMAND || + comparam == IP_EXEC_STOP; + clean = bool_param(j->intparams[IP_EXEC_CLEAN]); + username = string_param(j->intparams[injail + ? IP_EXEC_JAIL_USER : IP_EXEC_SYSTEM_USER]); + sjuser = bool_param(j->intparams[IP_EXEC_SYSTEM_JAIL_USER]); + + consfd = 0; + if (injail && + (conslog = string_param(j->intparams[IP_EXEC_CONSOLELOG]))) { + if (check_path(j, "exec.consolelog", conslog, 1, NULL) < 0) + return -1; + consfd = + open(conslog, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE); + if (consfd < 0) { + jail_warnx(j, "open %s: %s", conslog, strerror(errno)); + return -1; + } + } + + comlen = 0; + for (i = 0; argv[i]; i++) + comlen += strlen(argv[i]) + 1; + j->comline = cs = emalloc(comlen); + for (i = 0; argv[i]; i++) { + strcpy(cs, argv[i]); + if (argv[i + 1]) { + cs += strlen(argv[i]) + 1; + cs[-1] = ' '; + } + } + if (verbose > 0) + jail_note(j, "run command%s%s%s: %s\n", + injail ? " in jail" : "", username ? " as " : "", + username ? username : "", j->comline); + + pid = fork(); + if (pid < 0) + err(1, "fork"); + if (pid > 0) { + if (bg) { + free(j->comline); + j->comline = NULL; + return 0; + } else { + paralimit--; + add_proc(j, pid); + return 1; + } + } + if (bg) + setsid(); + + /* Set up the environment and run the command */ + pwd = NULL; + lcap = NULL; + if ((clean || username) && injail && sjuser && + get_user_info(j, username, &pwd, &lcap) < 0) + exit(1); + if (injail) { + /* jail_attach won't chdir along with its chroot. */ + path = string_param(j->intparams[KP_PATH]); + if (path && chdir(path) < 0) { + jail_warnx(j, "chdir %s: %s", path, strerror(errno)); + exit(1); + } + if (int_param(j->intparams[IP_EXEC_FIB], &fib) && + setfib(fib) < 0) { + jail_warnx(j, "setfib: %s", strerror(errno)); + exit(1); + } + if (jail_attach(j->jid) < 0) { + jail_warnx(j, "jail_attach: %s", strerror(errno)); + exit(1); + } + } + if (clean || username) { + if (!(injail && sjuser) && + get_user_info(j, username, &pwd, &lcap) < 0) + exit(1); + if (clean) { + term = getenv("TERM"); + environ = &cleanenv; + setenv("PATH", "/bin:/usr/bin", 0); + setenv("TERM", term, 1); + } + if (setusercontext(lcap, pwd, pwd->pw_uid, username + ? LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN + : LOGIN_SETPATH | LOGIN_SETENV) < 0) { + jail_warnx(j, "setusercontext %s: %s", pwd->pw_name, + strerror(errno)); + exit(1); + } + login_close(lcap); + setenv("USER", pwd->pw_name, 1); + setenv("HOME", pwd->pw_dir, 1); + setenv("SHELL", + *pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL, 1); + if (clean && chdir(pwd->pw_dir) < 0) { + jail_warnx(j, "chdir %s: %s", + pwd->pw_dir, strerror(errno)); + exit(1); + } + endpwent(); + } + + if (consfd != 0 && (dup2(consfd, 1) < 0 || dup2(consfd, 2) < 0)) { + jail_warnx(j, "exec.consolelog: %s", strerror(errno)); + exit(1); + } + closefrom(3); + execvp(argv[0], argv); + jail_warnx(j, "exec %s: %s", argv[0], strerror(errno)); + exit(1); +} + +/* + * Add a process to the hash, tied to a jail. + */ +static void +add_proc(struct cfjail *j, pid_t pid) +{ + struct kevent ke; + struct cfjail *tj; + struct phash *ph; + + if (!kq && (kq = kqueue()) < 0) + err(1, "kqueue"); + EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); + if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) + err(1, "kevent"); + ph = emalloc(sizeof(struct phash)); + ph->j = j; + ph->pid = pid; + LIST_INSERT_HEAD(&phash[pid % PHASH_SIZE], ph, le); + j->nprocs++; + j->flags |= JF_SLEEPQ; + if (j->timeout.tv_sec == 0) + requeue(j, &sleeping); + else { + /* File the jail in the sleep queue acording to its timeout. */ + TAILQ_REMOVE(j->queue, j, tq); + TAILQ_FOREACH(tj, &sleeping, tq) { + if (!tj->timeout.tv_sec || + j->timeout.tv_sec < tj->timeout.tv_sec || + (j->timeout.tv_sec == tj->timeout.tv_sec && + j->timeout.tv_nsec <= tj->timeout.tv_nsec)) { + TAILQ_INSERT_BEFORE(tj, j, tq); + break; + } + } + if (tj == NULL) + TAILQ_INSERT_TAIL(&sleeping, j, tq); + j->queue = &sleeping; + } +} + +/* + * Remove any processes from the hash that correspond to a jail. + */ +static void +clear_procs(struct cfjail *j) +{ + struct kevent ke; + struct phash *ph, *tph; + int i; + + j->nprocs = 0; + for (i = 0; i < PHASH_SIZE; i++) + LIST_FOREACH_SAFE(ph, &phash[i], le, tph) + if (ph->j == j) { + EV_SET(&ke, ph->pid, EVFILT_PROC, EV_DELETE, + NOTE_EXIT, 0, NULL); + (void)kevent(kq, &ke, 1, NULL, 0, NULL); + LIST_REMOVE(ph, le); + free(ph); + } +} + +/* + * Find the jail that corresponds to an exited process. + */ +static struct cfjail * +find_proc(pid_t pid) +{ + struct cfjail *j; + struct phash *ph; + + LIST_FOREACH(ph, &phash[pid % PHASH_SIZE], le) + if (ph->pid == pid) { + j = ph->j; + LIST_REMOVE(ph, le); + free(ph); + return --j->nprocs ? NULL : j; + } + return NULL; +} + +/* + * Send SIGTERM to all processes in a jail and wait for them to die. + */ +static int +term_procs(struct cfjail *j) +{ + struct kinfo_proc *ki; + int i, noted, pcnt, timeout; + + static kvm_t *kd; + + if (!int_param(j->intparams[IP_STOP_TIMEOUT], &timeout)) + timeout = DEFAULT_STOP_TIMEOUT; + else if (timeout == 0) + return 0; + + if (kd == NULL) { + kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); + if (kd == NULL) + return 0; + } + + ki = kvm_getprocs(kd, KERN_PROC_PROC, 0, &pcnt); + if (ki == NULL) + return 0; + noted = 0; + for (i = 0; i < pcnt; i++) + if (ki[i].ki_jid == j->jid && + kill(ki[i].ki_pid, SIGTERM) == 0) { + add_proc(j, ki[i].ki_pid); + if (verbose > 0) { + if (!noted) { + noted = 1; + jail_note(j, "sent SIGTERM to:"); + } + printf(" %d", ki[i].ki_pid); + } + } + if (noted) + printf("\n"); + if (j->nprocs > 0) { + clock_gettime(CLOCK_REALTIME, &j->timeout); + j->timeout.tv_sec += timeout; + return 1; + } + return 0; +} + +/* + * Look up a user in the passwd and login.conf files. + */ +static int +get_user_info(struct cfjail *j, const char *username, + const struct passwd **pwdp, login_cap_t **lcapp) +{ + const struct passwd *pwd; + + *pwdp = pwd = username ? getpwnam(username) : getpwuid(getuid()); + if (pwd == NULL) { + if (errno) + jail_warnx(j, "getpwnam%s%s: %s", username ? " " : "", + username ? username : "", strerror(errno)); + else if (username) + jail_warnx(j, "%s: no such user", username); + else + jail_warnx(j, "unknown uid %d", getuid()); + return -1; + } + *lcapp = login_getpwclass(pwd); + if (*lcapp == NULL) { + jail_warnx(j, "getpwclass %s: %s", pwd->pw_name, + strerror(errno)); + return -1; + } + /* Set the groups while the group file is still available */ + if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) { + jail_warnx(j, "initgroups %s: %s", pwd->pw_name, + strerror(errno)); + return -1; + } + return 0; +} + +/* + * Make sure a mount or consolelog path is a valid absolute pathname + * with no symlinks. + */ +static int +check_path(struct cfjail *j, const char *pname, const char *path, int isfile, + const char *umount_type) +{ + struct stat st, mpst; + struct statfs stfs; + char *tpath, *p; + const char *jailpath; + size_t jplen; + + if (path[0] != '/') { + jail_warnx(j, "%s: %s: not an absolute pathname", + pname, path); + return -1; + } + /* + * Only check for symlinks in components below the jail's path, + * since that's where the security risk lies. + */ + jailpath = string_param(j->intparams[KP_PATH]); + if (jailpath == NULL) + jailpath = ""; + jplen = strlen(jailpath); + if (!strncmp(path, jailpath, jplen) && path[jplen] == '/') { + tpath = alloca(strlen(path) + 1); + strcpy(tpath, path); + for (p = tpath + jplen; p != NULL; ) { + p = strchr(p + 1, '/'); + if (p) + *p = '\0'; + if (lstat(tpath, &st) < 0) { + if (errno == ENOENT && isfile && !p) + break; + jail_warnx(j, "%s: %s: %s", pname, tpath, + strerror(errno)); + return -1; + } + if (S_ISLNK(st.st_mode)) { + jail_warnx(j, "%s: %s is a symbolic link", + pname, tpath); + return -1; + } + if (p) + *p = '/'; + } + } + if (umount_type != NULL) { + if (stat(path, &st) < 0 || statfs(path, &stfs) < 0) { + jail_warnx(j, "%s: %s: %s", pname, path, + strerror(errno)); + return -1; + } + if (stat(stfs.f_mntonname, &mpst) < 0) { + jail_warnx(j, "%s: %s: %s", pname, stfs.f_mntonname, + strerror(errno)); + return -1; + } + if (st.st_ino != mpst.st_ino) { + jail_warnx(j, "%s: %s: not a mount point", + pname, path); + return -1; + } + if (strcmp(stfs.f_fstypename, umount_type)) { + jail_warnx(j, "%s: %s: not a %s mount", + pname, path, umount_type); + return -1; + } + } + return 0; +} Property changes on: stable/9/usr.sbin/jail/command.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/9/usr.sbin/jail/config.c =================================================================== --- stable/9/usr.sbin/jail/config.c (nonexistent) +++ stable/9/usr.sbin/jail/config.c (revision 235839) @@ -0,0 +1,831 @@ +/*- + * Copyright (c) 2011 James Gritton + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "jailp.h" + +struct ipspec { + const char *name; + unsigned flags; +}; + +extern FILE *yyin; +extern int yynerrs; + +struct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails); + +static void free_param(struct cfparams *pp, struct cfparam *p); +static void free_param_strings(struct cfparam *p); + +static const struct ipspec intparams[] = { + [IP_ALLOW_DYING] = {"allow.dying", PF_INTERNAL | PF_BOOL}, + [IP_COMMAND] = {"command", PF_INTERNAL}, + [IP_DEPEND] = {"depend", PF_INTERNAL}, + [IP_EXEC_CLEAN] = {"exec.clean", PF_INTERNAL | PF_BOOL}, + [IP_EXEC_CONSOLELOG] = {"exec.consolelog", PF_INTERNAL}, + [IP_EXEC_FIB] = {"exec.fib", PF_INTERNAL | PF_INT}, + [IP_EXEC_JAIL_USER] = {"exec.jail_user", PF_INTERNAL}, + [IP_EXEC_POSTSTART] = {"exec.poststart", PF_INTERNAL}, + [IP_EXEC_POSTSTOP] = {"exec.poststop", PF_INTERNAL}, + [IP_EXEC_PRESTART] = {"exec.prestart", PF_INTERNAL}, + [IP_EXEC_PRESTOP] = {"exec.prestop", PF_INTERNAL}, + [IP_EXEC_START] = {"exec.start", PF_INTERNAL}, + [IP_EXEC_STOP] = {"exec.stop", PF_INTERNAL}, + [IP_EXEC_SYSTEM_JAIL_USER]= {"exec.system_jail_user", + PF_INTERNAL | PF_BOOL}, + [IP_EXEC_SYSTEM_USER] = {"exec.system_user", PF_INTERNAL}, + [IP_EXEC_TIMEOUT] = {"exec.timeout", PF_INTERNAL | PF_INT}, +#if defined(INET) || defined(INET6) + [IP_INTERFACE] = {"interface", PF_INTERNAL}, + [IP_IP_HOSTNAME] = {"ip_hostname", PF_INTERNAL | PF_BOOL}, +#endif + [IP_MOUNT] = {"mount", PF_INTERNAL}, + [IP_MOUNT_DEVFS] = {"mount.devfs", PF_INTERNAL | PF_BOOL}, + [IP_MOUNT_FSTAB] = {"mount.fstab", PF_INTERNAL}, + [IP_STOP_TIMEOUT] = {"stop.timeout", PF_INTERNAL | PF_INT}, + [IP_VNET_INTERFACE] = {"vnet.interface", PF_INTERNAL}, +#ifdef INET + [IP__IP4_IFADDR] = {"ip4.addr", PF_INTERNAL | PF_CONV}, +#endif +#ifdef INET6 + [IP__IP6_IFADDR] = {"ip6.addr", PF_INTERNAL | PF_CONV}, +#endif + [IP__MOUNT_FROM_FSTAB] = {"mount.fstab", PF_INTERNAL | PF_CONV}, + [IP__OP] = {NULL, PF_CONV}, + [KP_ALLOW_CHFLAGS] = {"allow.chflags", 0}, + [KP_ALLOW_MOUNT] = {"allow.mount", 0}, + [KP_ALLOW_RAW_SOCKETS] = {"allow.raw_sockets", 0}, + [KP_ALLOW_SET_HOSTNAME]= {"allow.set_hostname", 0}, + [KP_ALLOW_SOCKET_AF] = {"allow.socket_af", 0}, + [KP_ALLOW_SYSVIPC] = {"allow.sysvipc", 0}, + [KP_DEVFS_RULESET] = {"devfs_ruleset", 0}, + [KP_ENFORCE_STATFS] = {"enforce_statfs", 0}, + [KP_HOST_HOSTNAME] = {"host.hostname", 0}, +#ifdef INET + [KP_IP4_ADDR] = {"ip4.addr", 0}, +#endif +#ifdef INET6 + [KP_IP6_ADDR] = {"ip6.addr", 0}, +#endif + [KP_JID] = {"jid", 0}, + [KP_NAME] = {"name", 0}, + [KP_PATH] = {"path", 0}, + [KP_PERSIST] = {"persist", 0}, + [KP_SECURELEVEL] = {"securelevel", 0}, + [KP_VNET] = {"vnet", 0}, +}; + +/* + * Parse the jail configuration file. + */ +void +load_config(void) +{ + struct cfjails wild; + struct cfparams opp; + struct cfjail *j, *tj, *wj; + struct cfparam *p, *vp, *tp; + struct cfstring *s, *vs, *ns; + struct cfvar *v; + char *ep; + size_t varoff; + int did_self, jseq, pgen; + + if (!strcmp(cfname, "-")) { + cfname = "STDIN"; + yyin = stdin; + } else { + yyin = fopen(cfname, "r"); + if (!yyin) + err(1, "%s", cfname); + } + if (yyparse() || yynerrs) + exit(1); + + /* Separate the wildcard jails out from the actual jails. */ + jseq = 0; + TAILQ_INIT(&wild); + TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { + j->seq = ++jseq; + if (wild_jail_name(j->name)) + requeue(j, &wild); + } + + TAILQ_FOREACH(j, &cfjails, tq) { + /* Set aside the jail's parameters. */ + TAILQ_INIT(&opp); + TAILQ_CONCAT(&opp, &j->params, tq); + /* + * The jail name implies its "name" or "jid" parameter, + * though they may also be explicitly set later on. + */ + add_param(j, NULL, + strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME, + j->name); + /* + * Collect parameters for the jail, global parameters/variables, + * and any matching wildcard jails. + */ + did_self = 0; + TAILQ_FOREACH(wj, &wild, tq) { + if (j->seq < wj->seq && !did_self) { + TAILQ_FOREACH(p, &opp, tq) + add_param(j, p, 0, NULL); + did_self = 1; + } + if (wild_jail_match(j->name, wj->name)) + TAILQ_FOREACH(p, &wj->params, tq) + add_param(j, p, 0, NULL); + } + if (!did_self) + TAILQ_FOREACH(p, &opp, tq) + add_param(j, p, 0, NULL); + + /* Resolve any variable substitutions. */ + pgen = 0; + TAILQ_FOREACH(p, &j->params, tq) { + p->gen = ++pgen; + find_vars: + TAILQ_FOREACH(s, &p->val, tq) { + varoff = 0; + while ((v = STAILQ_FIRST(&s->vars))) { + TAILQ_FOREACH(vp, &j->params, tq) + if (!strcmp(vp->name, v->name)) + break; + if (!vp) { + jail_warnx(j, + "%s: variable \"%s\" not found", + p->name, v->name); + bad_var: + j->flags |= JF_FAILED; + TAILQ_FOREACH(vp, &j->params, tq) + if (vp->gen == pgen) + vp->flags |= PF_BAD; + goto free_var; + } + if (vp->flags & PF_BAD) + goto bad_var; + if (vp->gen == pgen) { + jail_warnx(j, "%s: variable loop", + v->name); + goto bad_var; + } + TAILQ_FOREACH(vs, &vp->val, tq) + if (!STAILQ_EMPTY(&vs->vars)) { + vp->gen = pgen; + TAILQ_REMOVE(&j->params, vp, + tq); + TAILQ_INSERT_BEFORE(p, vp, tq); + p = vp; + goto find_vars; + } + vs = TAILQ_FIRST(&vp->val); + if (TAILQ_NEXT(vs, tq) != NULL && + (s->s[0] != '\0' || + STAILQ_NEXT(v, tq))) { + jail_warnx(j, "%s: array cannot be " + "substituted inline", + p->name); + goto bad_var; + } + s->s = erealloc(s->s, s->len + vs->len + 1); + memmove(s->s + v->pos + varoff + vs->len, + s->s + v->pos + varoff, + s->len - (v->pos + varoff) + 1); + memcpy(s->s + v->pos + varoff, vs->s, vs->len); + varoff += vs->len; + s->len += vs->len; + while ((vs = TAILQ_NEXT(vs, tq))) { + ns = emalloc(sizeof(struct cfstring)); + ns->s = estrdup(vs->s); + ns->len = vs->len; + STAILQ_INIT(&ns->vars); + TAILQ_INSERT_AFTER(&p->val, s, ns, tq); + s = ns; + } + free_var: + free(v->name); + STAILQ_REMOVE_HEAD(&s->vars, tq); + free(v); + } + } + } + + /* Free the jail's original parameter list and any variables. */ + while ((p = TAILQ_FIRST(&opp))) + free_param(&opp, p); + TAILQ_FOREACH_SAFE(p, &j->params, tq, tp) + if (p->flags & PF_VAR) + free_param(&j->params, p); + } + while ((wj = TAILQ_FIRST(&wild))) { + free(wj->name); + while ((p = TAILQ_FIRST(&wj->params))) + free_param(&wj->params, p); + TAILQ_REMOVE(&wild, wj, tq); + } +} + +/* + * Create a new jail record. + */ +struct cfjail * +add_jail(void) +{ + struct cfjail *j; + + j = emalloc(sizeof(struct cfjail)); + memset(j, 0, sizeof(struct cfjail)); + TAILQ_INIT(&j->params); + STAILQ_INIT(&j->dep[DEP_FROM]); + STAILQ_INIT(&j->dep[DEP_TO]); + j->queue = &cfjails; + TAILQ_INSERT_TAIL(&cfjails, j, tq); + return j; +} + +/* + * Add a parameter to a jail. + */ +void +add_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum, + const char *value) +{ + struct cfstrings nss; + struct cfparam *dp, *np; + struct cfstring *s, *ns; + struct cfvar *v, *nv; + const char *name; + char *cs, *tname; + unsigned flags; + + if (j == NULL) { + /* Create a single anonymous jail if one doesn't yet exist. */ + j = TAILQ_LAST(&cfjails, cfjails); + if (j == NULL) + j = add_jail(); + } + TAILQ_INIT(&nss); + if (p != NULL) { + name = p->name; + flags = p->flags; + /* + * Make a copy of the parameter's string list, + * which may be freed if it's overridden later. + */ + TAILQ_FOREACH(s, &p->val, tq) { + ns = emalloc(sizeof(struct cfstring)); + ns->s = estrdup(s->s); + ns->len = s->len; + STAILQ_INIT(&ns->vars); + STAILQ_FOREACH(v, &s->vars, tq) { + nv = emalloc(sizeof(struct cfvar)); + nv->name = strdup(v->name); + nv->pos = v->pos; + STAILQ_INSERT_TAIL(&ns->vars, nv, tq); + } + TAILQ_INSERT_TAIL(&nss, ns, tq); + } + } else { + flags = PF_APPEND; + if (ipnum != IP__NULL) { + name = intparams[ipnum].name; + flags |= intparams[ipnum].flags; + } else if ((cs = strchr(value, '='))) { + tname = alloca(cs - value + 1); + strlcpy(tname, value, cs - value + 1); + name = tname; + value = cs + 1; + } else { + name = value; + value = NULL; + } + if (value != NULL) { + ns = emalloc(sizeof(struct cfstring)); + ns->s = estrdup(value); + ns->len = strlen(value); + STAILQ_INIT(&ns->vars); + TAILQ_INSERT_TAIL(&nss, ns, tq); + } + } + + /* See if this parameter has already been added. */ + if (ipnum != IP__NULL) + dp = j->intparams[ipnum]; + else + TAILQ_FOREACH(dp, &j->params, tq) + if (!(dp->flags & PF_CONV) && equalopts(dp->name, name)) + break; + if (dp != NULL) { + /* Found it - append or replace. */ + if (strcmp(dp->name, name)) { + free(dp->name); + dp->name = estrdup(name); + } + if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss)) + free_param_strings(dp); + TAILQ_CONCAT(&dp->val, &nss, tq); + dp->flags |= flags; + } else { + /* Not found - add it. */ + np = emalloc(sizeof(struct cfparam)); + np->name = estrdup(name); + TAILQ_INIT(&np->val); + TAILQ_CONCAT(&np->val, &nss, tq); + np->flags = flags; + np->gen = 0; + TAILQ_INSERT_TAIL(&j->params, np, tq); + if (ipnum != IP__NULL) + j->intparams[ipnum] = np; + else + for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++) + if (!(intparams[ipnum].flags & PF_CONV) && + equalopts(name, intparams[ipnum].name)) { + j->intparams[ipnum] = np; + np->flags |= intparams[ipnum].flags; + break; + } + } +} + +/* + * Return if a boolean parameter exists and is true. + */ +int +bool_param(const struct cfparam *p) +{ + const char *cs; + + if (p == NULL) + return 0; + cs = strrchr(p->name, '.'); + return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^ + (TAILQ_EMPTY(&p->val) || + !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") || + (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10))); +} + +/* + * Set an integer if a parameter if it exists. + */ +int +int_param(const struct cfparam *p, int *ip) +{ + if (p == NULL || TAILQ_EMPTY(&p->val)) + return 0; + *ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10); + return 1; +} + +/* + * Return the string value of a scalar parameter if it exists. + */ +const char * +string_param(const struct cfparam *p) +{ + return (p && !TAILQ_EMPTY(&p->val) + ? TAILQ_LAST(&p->val, cfstrings)->s : NULL); +} + +/* + * Check syntax and values of internal parameters. Set some internal + * parameters based on the values of others. + */ +int +check_intparams(struct cfjail *j) +{ + struct cfparam *p; + struct cfstring *s; + FILE *f; + const char *val; + char *cs, *ep, *ln; + size_t lnlen; + int error; +#if defined(INET) || defined(INET6) + struct addrinfo hints; + struct addrinfo *ai0, *ai; + const char *hostname; + int gicode, defif, prefix; +#endif +#ifdef INET + struct in_addr addr4; + int ip4ok; + char avalue4[INET_ADDRSTRLEN]; +#endif +#ifdef INET6 + struct in6_addr addr6; + int ip6ok; + char avalue6[INET6_ADDRSTRLEN]; +#endif + + error = 0; + /* Check format of boolan and integer values. */ + TAILQ_FOREACH(p, &j->params, tq) { + if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) { + val = TAILQ_LAST(&p->val, cfstrings)->s; + if (p->flags & PF_BOOL) { + if (strcasecmp(val, "false") && + strcasecmp(val, "true") && + ((void)strtol(val, &ep, 10), *ep)) { + jail_warnx(j, + "%s: unknown boolean value \"%s\"", + p->name, val); + error = -1; + } + } else { + (void)strtol(val, &ep, 10); + if (ep == val || *ep) { + jail_warnx(j, + "%s: non-integer value \"%s\"", + p->name, val); + error = -1; + } + } + } + } + +#if defined(INET) || defined(INET6) + /* + * The ip_hostname parameter looks up the hostname, and adds parameters + * for any IP addresses it finds. + */ + if (((j->flags & JF_OP_MASK) != JF_STOP || + j->intparams[IP_INTERFACE] != NULL) && + bool_param(j->intparams[IP_IP_HOSTNAME]) && + (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) { + j->intparams[IP_IP_HOSTNAME] = NULL; + /* + * Silently ignore unsupported address families from + * DNS lookups. + */ +#ifdef INET + ip4ok = feature_present("inet"); +#endif +#ifdef INET6 + ip6ok = feature_present("inet6"); +#endif + if ( +#if defined(INET) && defined(INET6) + ip4ok || ip6ok +#elif defined(INET) + ip4ok +#elif defined(INET6) + ip6ok +#endif + ) { + /* Look up the hostname (or get the address) */ + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = +#if defined(INET) && defined(INET6) + ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) : PF_INET6; +#elif defined(INET) + PF_INET; +#elif defined(INET6) + PF_INET6; +#endif + gicode = getaddrinfo(hostname, NULL, &hints, &ai0); + if (gicode != 0) { + jail_warnx(j, "host.hostname %s: %s", hostname, + gai_strerror(gicode)); + error = -1; + } else { + /* + * Convert the addresses to ASCII so jailparam + * can convert them back. Errors are not + * expected here. + */ + for (ai = ai0; ai; ai = ai->ai_next) + switch (ai->ai_family) { +#ifdef INET + case AF_INET: + memcpy(&addr4, + &((struct sockaddr_in *) + (void *)ai->ai_addr)-> + sin_addr, sizeof(addr4)); + if (inet_ntop(AF_INET, + &addr4, avalue4, + INET_ADDRSTRLEN) == NULL) + err(1, "inet_ntop"); + add_param(j, NULL, KP_IP4_ADDR, + avalue4); + break; +#endif +#ifdef INET6 + case AF_INET6: + memcpy(&addr6, + &((struct sockaddr_in6 *) + (void *)ai->ai_addr)-> + sin6_addr, sizeof(addr6)); + if (inet_ntop(AF_INET6, + &addr6, avalue6, + INET6_ADDRSTRLEN) == NULL) + err(1, "inet_ntop"); + add_param(j, NULL, KP_IP6_ADDR, + avalue6); + break; +#endif + } + freeaddrinfo(ai0); + } + } + } + + /* + * IP addresses may include an interface to set that address on, + * and a netmask/suffix for that address. + */ + defif = string_param(j->intparams[IP_INTERFACE]) != NULL; +#ifdef INET + if (j->intparams[KP_IP4_ADDR] != NULL) { + TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) { + cs = strchr(s->s, '|'); + if (cs || defif) + add_param(j, NULL, IP__IP4_IFADDR, s->s); + if (cs) { + strcpy(s->s, cs + 1); + s->len -= cs + 1 - s->s; + } + if ((cs = strchr(s->s, '/'))) { + prefix = strtol(cs + 1, &ep, 10); + if (*ep == '.' + ? inet_pton(AF_INET, cs + 1, &addr4) != 1 + : *ep || prefix < 0 || prefix > 32) { + jail_warnx(j, + "ip4.addr: bad netmask \"%s\"", cs); + error = -1; + } + *cs = '\0'; + s->len = cs - s->s + 1; + } + } + } +#endif +#ifdef INET6 + if (j->intparams[KP_IP6_ADDR] != NULL) { + TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) { + cs = strchr(s->s, '|'); + if (cs || defif) + add_param(j, NULL, IP__IP6_IFADDR, s->s); + if (cs) { + strcpy(s->s, cs + 1); + s->len -= cs + 1 - s->s; + } + if ((cs = strchr(s->s, '/'))) { + prefix = strtol(cs + 1, &ep, 10); + if (*ep || prefix < 0 || prefix > 128) { + jail_warnx(j, + "ip6.addr: bad prefixlen \"%s\"", + cs); + error = -1; + } + *cs = '\0'; + s->len = cs - s->s + 1; + } + } + } +#endif +#endif + + /* + * Read mount.fstab file(s), and treat each line as its own mount + * parameter. + */ + if (j->intparams[IP_MOUNT_FSTAB] != NULL) { + TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) { + if (s->len == 0) + continue; + f = fopen(s->s, "r"); + if (f == NULL) { + jail_warnx(j, "mount.fstab: %s: %s", + s->s, strerror(errno)); + error = -1; + continue; + } + while ((ln = fgetln(f, &lnlen))) { + if ((cs = memchr(ln, '#', lnlen - 1))) + lnlen = cs - ln + 1; + if (ln[lnlen - 1] == '\n' || + ln[lnlen - 1] == '#') + ln[lnlen - 1] = '\0'; + else { + cs = alloca(lnlen + 1); + strlcpy(cs, ln, lnlen + 1); + ln = cs; + } + add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln); + } + fclose(f); + } + } + if (error) + failed(j); + return error; +} + +/* + * Import parameters into libjail's binary jailparam format. + */ +int +import_params(struct cfjail *j) +{ + struct cfparam *p; + struct cfstring *s, *ts; + struct jailparam *jp; + char *value, *cs; + size_t vallen; + int error; + + error = 0; + j->njp = 0; + TAILQ_FOREACH(p, &j->params, tq) + if (!(p->flags & PF_INTERNAL)) + j->njp++; + j->jp = jp = emalloc(j->njp * sizeof(struct jailparam)); + TAILQ_FOREACH(p, &j->params, tq) { + if (p->flags & PF_INTERNAL) + continue; + if (jailparam_init(jp, p->name) < 0) { + error = -1; + jail_warnx(j, "%s", jail_errmsg); + continue; + } + if (TAILQ_EMPTY(&p->val)) + value = NULL; + else if (!jp->jp_elemlen || + !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) { + /* + * Scalar parameters silently discard multiple (array) + * values, keeping only the last value added. This + * lets values added from the command line append to + * arrays wthout pre-checking the type. + */ + value = TAILQ_LAST(&p->val, cfstrings)->s; + } else { + /* + * Convert arrays into comma-separated strings, which + * jailparam_import will then convert back into arrays. + */ + vallen = 0; + TAILQ_FOREACH(s, &p->val, tq) + vallen += s->len + 1; + value = alloca(vallen); + cs = value; + TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { + strcpy(cs, s->s); + if (ts != NULL) { + cs += s->len + 1; + cs[-1] = ','; + } + } + } + if (jailparam_import(jp, value) < 0) { + error = -1; + jail_warnx(j, "%s", jail_errmsg); + } + jp++; + } + if (error) { + jailparam_free(j->jp, j->njp); + free(j->jp); + j->jp = NULL; + failed(j); + } + return error; +} + +/* + * Check if options are equal (with or without the "no" prefix). + */ +int +equalopts(const char *opt1, const char *opt2) +{ + char *p; + + /* "opt" vs. "opt" or "noopt" vs. "noopt" */ + if (strcmp(opt1, opt2) == 0) + return (1); + /* "noopt" vs. "opt" */ + if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) + return (1); + /* "opt" vs. "noopt" */ + if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) + return (1); + while ((p = strchr(opt1, '.')) != NULL && + !strncmp(opt1, opt2, ++p - opt1)) { + opt2 += p - opt1; + opt1 = p; + /* "foo.noopt" vs. "foo.opt" */ + if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) + return (1); + /* "foo.opt" vs. "foo.noopt" */ + if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) + return (1); + } + return (0); +} + +/* + * See if a jail name matches a wildcard. + */ +int +wild_jail_match(const char *jname, const char *wname) +{ + const char *jc, *jd, *wc, *wd; + + /* + * A non-final "*" component in the wild name matches a single jail + * component, and a final "*" matches one or more jail components. + */ + for (jc = jname, wc = wname; + (jd = strchr(jc, '.')) && (wd = strchr(wc, '.')); + jc = jd + 1, wc = wd + 1) + if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2)) + return 0; + return (!strcmp(jc, wc) || !strcmp(wc, "*")); +} + +/* + * Return if a jail name is a wildcard. + */ +int +wild_jail_name(const char *wname) +{ + const char *wc; + + for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*')) + if ((wc == wname || wc[-1] == '.') && + (wc[1] == '\0' || wc[1] == '.')) + return 1; + return 0; +} + +/* + * Free a parameter record and all its strings and variables. + */ +static void +free_param(struct cfparams *pp, struct cfparam *p) +{ + free(p->name); + free_param_strings(p); + TAILQ_REMOVE(pp, p, tq); + free(p); +} + +static void +free_param_strings(struct cfparam *p) +{ + struct cfstring *s; + struct cfvar *v; + + while ((s = TAILQ_FIRST(&p->val))) { + free(s->s); + while ((v = STAILQ_FIRST(&s->vars))) { + free(v->name); + STAILQ_REMOVE_HEAD(&s->vars, tq); + free(v); + } + TAILQ_REMOVE(&p->val, s, tq); + free(s); + } +} Property changes on: stable/9/usr.sbin/jail/config.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/9/usr.sbin/jail/jail.8 =================================================================== --- stable/9/usr.sbin/jail/jail.8 (revision 235838) +++ stable/9/usr.sbin/jail/jail.8 (revision 235839) @@ -1,973 +1,1226 @@ -.\" .\" Copyright (c) 2000, 2003 Robert N. M. Watson -.\" Copyright (c) 2008 James Gritton +.\" Copyright (c) 2008-2012 James Gritton .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" -.\" ---------------------------------------------------------------------------- -.\" "THE BEER-WARE LICENSE" (Revision 42): -.\" wrote this file. As long as you retain this notice you -.\" can do whatever you want with this stuff. If we meet some day, and you think -.\" this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp -.\" ---------------------------------------------------------------------------- -.\" .\" $FreeBSD$ .\" -.Dd February 29, 2012 +.Dd May 23, 2012 .Dt JAIL 8 .Os .Sh NAME .Nm jail -.Nd "create or modify a system jail" +.Nd "manage system jails" .Sh SYNOPSIS .Nm -.Op Fl dhi +.Op Fl dhilqv .Op Fl J Ar jid_file -.Op Fl l u Ar username | Fl U Ar username -.Op Fl c | m -.Op Ar parameter=value ... +.Op Fl u Ar username +.Op Fl U Ar username +.Op Fl cmr +.Ar param Ns = Ns Ar value ... +.Op Cm command Ns = Ns Ar command ... .Nm -.Op Fl hi -.Op Fl n Ar jailname +.Op Fl dqv +.Op Fl f Ar conf_file +.Op Fl p Ar limit +.Op Fl cmr +.Op Ar jail +.Nm +.Op Fl qv +.Op Fl f Ar conf_file +.Op Fl rR +.Op Cm * | Ar jail ... +.Nm +.Op Fl dhilqv .Op Fl J Ar jid_file +.Op Fl u Ar username +.Op Fl U Ar username +.Op Fl n Ar jailname .Op Fl s Ar securelevel -.Op Fl l u Ar username | Fl U Ar username -.Op Ar path hostname [ip[,..]] command ... -.Nm -.Op Fl r Ar jail +.Op Ar path hostname [ Ar ip Ns [ Ns Ar ,... Ns ]] Ar command ... .Sh DESCRIPTION The .Nm -utility creates a new jail or modifies an existing jail, optionally -imprisoning the current process (and future descendants) inside it. +utility creates new jails, or modifies or removes existing jails. +A jail is specified via parameters on the command line, or in the +.Xr jail.conf 5 +file. .Pp -The options are as follows: +At least one of the options +.Fl c , +.Fl m +or +.Fl r +must be specified. +These options are used alone or in combination describe the operation to +perform: .Bl -tag -width indent +.It Fl c +Create a new jail. +The jail +.Va jid +and +.Va name +parameters (if specified) on the command line, +or any jails +must not refer to an existing jail. +.It Fl m +Modify an existing jail. +One of the +.Va jid +or +.Va name +parameters must exist and refer to an existing jail. +Some parameters may not be changed on a running jail. +.It Fl r +Remove the +.Ar jail +specified by jid or name. +All jailed processes are killed, and all children of this jail are also +removed. +.It Fl rc +Restart an existing jail. +The jail is first removed and then re-created, as if +.Dq Nm Fl c +and +.Dq Nm Fl r +were run in succession. +.It Fl cm +Create a jail if it does not exist, or modify the jail if it does exist. +.It Fl mr +Modify an existing jail. +The jail may be restarted if necessary to modify parameters than could +not otherwise be changed. +.It Fl cmr +Create a jail if it doesn't exist, or modify (and possibly restart) the +jail if it does exist. +.El +.Pp +Other available options are: +.Bl -tag -width indent .It Fl d -Allow making changes to a dying jail. +Allow making changes to a dying jail, equivalent to the +.Va allow.dying +parameter. +.It Fl f Ar conf_file +Use configuration file +.Ar conf_file +instead of the default +.Pa /etc/jail.conf . .It Fl h Resolve the .Va host.hostname parameter (or .Va hostname ) and add all IP addresses returned by the resolver -to the list of -.Va ip -addresses for this prison. -This may affect default address selection for outgoing IPv4 connections -of prisons. -The address first returned by the resolver for each address family -will be used as primary address. -See the -.Va ip4.addr -and -.Va ip6.addr -parameters further down for details. -.It Fl i -Output the jail identifier of the newly created jail. -.It Fl n Ar jailname -Set the jail's name. -This is deprecated and is equivalent to setting the -.Va name +to the list of addresses for this prison. +This is equivalent to the +.Va ip_hostname parameter. +.It Fl i +Output (only) the jail identifier of the newly created jail(s). +This implies the +.Fl q +option. .It Fl J Ar jid_file Write a .Ar jid_file -file, containing jail identifier, path, hostname, IP and -command used to start the jail. +file, containing parameters used to start the jail. .It Fl l -Run program in the clean environment. -The environment is discarded except for -.Ev HOME , SHELL , TERM -and -.Ev USER . -.Ev HOME -and -.Ev SHELL -are set to the target login's default values. -.Ev USER -is set to the target login. -.Ev TERM -is imported from the current environment. -The environment variables from the login class capability database for the -target login are also set. +Run commands in a clean environment. +This is deprecated and is equivalent to the exec.clean parameter. +.It Fl n Ar jailname +Set the jail's name. +This is deprecated and is equivalent to the +.Va name +parameter. +.It Fl p Ar limit +Limit the number of commands from +.Va exec.* +that can run simultaneously. +.It Fl q +Suppress the message printed whenever a jail is created, modified or removed. +Only error messages will be printed. +.It Fl R +A variation of the +.Fl r +option that removes an existing jail without using the configuration file. +No removal-related parameters for this jail will be used - the jail will +simply be removed. .It Fl s Ar securelevel Set the .Va kern.securelevel MIB entry to the specified value inside the newly created jail. -This is deprecated and is equivalent to setting the +This is deprecated and is equivalent to the .Va securelevel parameter. .It Fl u Ar username -The user name from host environment as whom the -.Ar command -should run. +The user name from host environment as whom jailed commands should run. +This is deprecated and is equivalent to the +.Va exec.jail_user +and +.Va exec.system_jail_user +parameters. .It Fl U Ar username -The user name from jailed environment as whom the -.Ar command -should run. -.It Fl c -Create a new jail. +The user name from jailed environment as whom jailed commands should run. +This is deprecated and is equivalent to the +.Va exec.jail_user +parameter. +.It Fl v +Print a message on every operation, such as running commands and +mounting filesystems. +.El +.Pp +If no arguments are given after the options, the operation (except +remove) will be performed on all jails specified in the +.Xr jail.conf 5 +file. +A single argument of a jail name will operate only on the specified jail. The -.Va jid +.Fl r and -.Va name -parameters (if specified) must not refer to an existing jail. -.It Fl m -Modify an existing jail. -One of the -.Va jid -or -.Va name -parameters must exist and refer to an existing jail. -.It Fl cm -Create a jail if it does not exist, or modify a jail if it does exist. -.It Fl r -Remove the -.Ar jail -specified by jid or name. -All jailed processes are killed, and all children of this jail are also -removed. -.El +.Fl R +options can also remove running jails that aren't in the +.Xr jail.conf 5 +file, specified by name or jid. .Pp -At least one of the -.Fl c , -.Fl m -or +An argument of +.Dq * +is a wildcard that will operate on all jails, regardless of whether +they appear in +.Xr jail.conf 5 ; +this is the surest way for .Fl r -options must be specified. +to remove all jails. +If hierarchical jails exist, a partial-matching wildcard definition may +be specified. +For example, an argument of +.Dq foo.* +would apply to jails with names like +.Dq foo.bar +and +.Dq foo.bar.baz . .Pp -.Ar Parameters -are listed in -.Dq name=value -form, following the options. -Some parameters are boolean, and do not have a value but are set by the -name alone with or without a -.Dq no -prefix, e.g. -.Va persist -or -.Va nopersist . -Any parameters not set will be given default values, often based on the -current environment. -.Pp -The pseudo-parameter -.Va command -specifies that the current process should enter the new (or modified) jail, -and run the specified command. -It must be the last parameter specified, because it includes not only -the value following the -.Sq = -sign, but also passes the rest of the arguments to the command. -.Pp -Instead of supplying named -.Ar parameters , -four fixed parameters may be supplied in order on the command line: +A jail may be specified with parameters directly on the command line. +In this case, the +.Xr jail.conf 5 +file will not be used. +For backward compatibility, the command line may also have four fixed +parameters, without names: .Ar path , .Ar hostname , .Ar ip , and .Ar command . -As the -.Va jid -and -.Va name -parameters aren't in this list, this mode will always create a new jail, and -the +This mode will always create a new jail, and the .Fl c and .Fl m options don't apply (and must not exist). +.Ss Jail Parameters +Parameters in the +.Xr jail.conf 5 +file, or on the command line, are generally in +.Dq name=value +form. +Some parameters are boolean, and do not have a value but are set by the +name alone with or without a +.Dq no +prefix, e.g. +.Va persist +or +.Va nopersist . +They can also be given the values +.Dq true +and +.Dq false . +Other partameters may have more than one value, specified as a +comma-separated list or with +.Dq += +in the configuration file (see +.Xr jail.conf 5 +for details). .Pp -Jails have a set a core parameters, and modules can add their own jail -parameters. +The +.Nm +utility recognizes two classes of parameters. There are the true jail +parameters that are passed to the kernel when the jail is created, +can be seen with +.Xr jls 8 , +and can (usually) be changed with +.Dq Nm Fl m. +Then there are pseudo-parameters that are only used by +.Nm +itself. +.Pp +Jails have a set a core parameters, and kernel modules can add their own +jail parameters. The current set of available parameters can be retrieved via .Dq Nm sysctl Fl d Va security.jail.param . +Any parameters not set will be given default values, often based on the +current environment. The core parameters are: .Bl -tag -width indent .It Va jid The jail identifier. This will be assigned automatically to a new jail (or can be explicitly set), and can be used to identify the jail for later modification, or for such commands as .Xr jls 8 or .Xr jexec 8 . .It Va name The jail name. This is an arbitrary string that identifies a jail (except it may not contain a .Sq \&. ) . Like the .Va jid , it can be passed to later .Nm commands, or to .Xr jls 8 or .Xr jexec 8 . If no .Va name is supplied, a default is assumed that is the same as the .Va jid . -.It Va path -Directory which is to be the root of the prison. The -.Va command -(if any) is run from this directory, as are commands from -.Xr jexec 8 . +.Va name +parameter is implied by the +.Xr jail.conf 5 +file format, and need not be explicitly set when using the configuration +file. +.It Va path +The directory which is to be the root of the prison. +Any commands run inside the prison, either by +.Nm +or from +.Xr jexec 8 , +are run from this directory. .It Va ip4.addr -A comma-separated list of IPv4 addresses assigned to the prison. +A list of IPv4 addresses assigned to the prison. If this is set, the jail is restricted to using only these addresses. Any attempts to use other addresses fail, and attempts to use wildcard addresses silently use the jailed address instead. For IPv4 the first address given will be kept used as the source address in case source address selection on unbound sockets cannot find a better match. It is only possible to start multiple jails with the same IP address, if none of the jails has more than this single overlapping IP address assigned to itself. .It Va ip4.saddrsel A boolean option to change the formerly mentioned behaviour and disable IPv4 source address selection for the prison in favour of the primary IPv4 address of the jail. -Source address selection is enabled by default for all jails and a +Source address selection is enabled by default for all jails and the .Va ip4.nosaddrsel setting of a parent jail is not inherited for any child jails. .It Va ip4 Control the availability of IPv4 addresses. Possible values are .Dq inherit to allow unrestricted access to all system addresses, .Dq new to restrict addresses via .Va ip4.addr above, and .Dq disable to stop the jail from using IPv4 entirely. Setting the .Va ip4.addr parameter implies a value of .Dq new . .It Va ip6.addr , Va ip6.saddrsel , Va ip6 A set of IPv6 options for the prison, the counterparts to .Va ip4.addr , .Va ip4.saddrsel and .Va ip4 above. +.It vnet +Create the prison with its own virtual network stack, +with its own network interfaces, addresses, routing table, etc. +The kernel must have been compiled with the +.Sy VIMAGE option +for this to be available. +Possible values are +.Dq inherit +to use the system network stack, possibly with restricted IP addresses, +and +.Dq new +to create a new network stack. .It Va host.hostname -Hostname of the prison. +The hostname of the prison. Other similar parameters are .Va host.domainname , .Va host.hostuuid and .Va host.hostid . .It Va host Set the origin of hostname and related information. Possible values are .Dq inherit to use the system information and .Dq new for the jail to use the information from the above fields. Setting any of the above fields implies a value of .Dq new . .It Va securelevel The value of the jail's .Va kern.securelevel sysctl. A jail never has a lower securelevel than the default system, but by setting this parameter it may have a higher one. If the system securelevel is changed, any jail securelevels will be at least as secure. .It Va devfs_ruleset The number of the devfs ruleset that is enforced for mounting devfs in this jail. A value of zero (default) means no ruleset is enforced. Descendant jails inherit the parent jail's devfs ruleset enforcement. Mounting devfs inside a jail is possible only if the .Va allow.mount and .Va allow.mount.devfs permissions are effective and .Va enforce_statfs is set to a value lower than 2. Devfs rules and rulesets cannot be viewed or modified from inside a jail. +.Pp +NOTE: It is important that only appropriate device nodes in devfs be +exposed to a jail; access to disk devices in the jail may permit processes +in the jail to bypass the jail sandboxing by modifying files outside of +the jail. +See +.Xr devfs 8 +for information on how to use devfs rules to limit access to entries +in the per-jail devfs. +A simple devfs ruleset for jails is available as ruleset #4 in +.Pa /etc/defaults/devfs.rules . .It Va children.max The number of child jails allowed to be created by this jail (or by other jails under this jail). This limit is zero by default, indicating the jail is not allowed to create child jails. See the -.Va "Hierarchical Jails" +.Sx "Hierarchical Jails" section for more information. .It Va children.cur The number of descendents of this jail, including its own child jails and any jails created under them. .It Va enforce_statfs This determines which information processes in a jail are able to get about mount points. It affects the behaviour of the following syscalls: .Xr statfs 2 , .Xr fstatfs 2 , .Xr getfsstat 2 and .Xr fhstatfs 2 (as well as similar compatibility syscalls). When set to 0, all mount points are available without any restrictions. When set to 1, only mount points below the jail's chroot directory are visible. In addition to that, the path to the jail's chroot directory is removed from the front of their pathnames. When set to 2 (default), above syscalls can operate only on a mount-point where the jail's chroot directory is located. .It Va persist Setting this boolean parameter allows a jail to exist without any processes. -Normally, a jail is destroyed as its last process exits. +Normally, a command is run as part of jail creation, and then the jail +is destroyed as its last process exits. A new jail must have either the .Va persist parameter or +.Va exec.start +or .Va command pseudo-parameter set. .It Va cpuset.id The ID of the cpuset associated with this jail (read-only). .It Va dying This is true if the jail is in the process of shutting down (read-only). .It Va parent The .Va jid of the parent of this jail, or zero if this is a top-level jail (read-only). .It Va allow.* Some restrictions of the jail environment may be set on a per-jail basis. With the exception of .Va allow.set_hostname , these boolean parameters are off by default. .Bl -tag -width indent .It Va allow.set_hostname The jail's hostname may be changed via .Xr hostname 1 or .Xr sethostname 3 . .It Va allow.sysvipc A process within the jail has access to System V IPC primitives. In the current jail implementation, System V primitives share a single namespace across the host and jail environments, meaning that processes within a jail would be able to communicate with (and potentially interfere with) processes outside of the jail, and in other jails. .It Va allow.raw_sockets The prison root is allowed to create raw sockets. Setting this parameter allows utilities like .Xr ping 8 and .Xr traceroute 8 to operate inside the prison. If this is set, the source IP addresses are enforced to comply with the IP address bound to the jail, regardless of whether or not the .Dv IP_HDRINCL flag has been set on the socket. Since raw sockets can be used to configure and interact with various network subsystems, extra caution should be used where privileged access to jails is given out to untrusted parties. .It Va allow.chflags Normally, privileged users inside a jail are treated as unprivileged by .Xr chflags 2 . When this parameter is set, such users are treated as privileged, and may manipulate system file flags subject to the usual constraints on .Va kern.securelevel . .It Va allow.mount privileged users inside the jail will be able to mount and unmount file system types marked as jail-friendly. The .Xr lsvfs 1 command can be used to find file system types available for mount from within a jail. This permission is effective only if .Va enforce_statfs is set to a value lower than 2. .It Va allow.mount.devfs privileged users inside the jail will be able to mount and unmount the devfs file system. This permission is effective only together with .Va allow.mount and if .Va enforce_statfs is set to a value lower than 2. Please consider restricting the devfs ruleset with the .Va devfs_ruleset option. .It Va allow.mount.nullfs privileged users inside the jail will be able to mount and unmount the nullfs file system. This permission is effective only together with .Va allow.mount and if .Va enforce_statfs is set to a value lower than 2. .It Va allow.mount.procfs privileged users inside the jail will be able to mount and unmount the procfs file system. This permission is effective only together with .Va allow.mount and if .Va enforce_statfs is set to a value lower than 2. .It Va allow.mount.zfs privileged users inside the jail will be able to mount and unmount the ZFS file system. This permission is effective only together with .Va allow.mount and if .Va enforce_statfs is set to a value lower than 2. See .Xr zfs 8 for information on how to configure the ZFS filesystem to operate from within a jail. .It Va allow.quotas The prison root may administer quotas on the jail's filesystem(s). This includes filesystems that the jail may share with other jails or with non-jailed parts of the system. .It Va allow.socket_af Sockets within a jail are normally restricted to IPv4, IPv6, local (UNIX), and route. This allows access to other protocol stacks that have not had jail functionality added to them. .El .El .Pp +There are pseudo-parameters that aren't passed to the kernel, but are +used by +.Nm +to set up the prison environment, often by running specified commands +when jails are created or removed. +The +.Va exec.* +command parameters are +.Xr sh 1 +command lines that are run in either the system or prison environment. +They may be given multiple values, which run would the specified +commands in sequence. +All commands must succed (return a zero exit status), or the jail will +not be created or removed. +.Pp +The pseudo-parameters are: +.Bl -tag -width indent +.It Va exec.prestart +Command(s) to run in the system environment before a prison is created. +.It Va exec.start +Command(s) to run in the prison environment when a jail is created. +A typical command to run is +.Dq sh /etc/rc . +.It Va command +A synonym for +.Va exec.start +for use when specifying a prison directly on the command line. +Unlike other parameters whose value is a single string, +.Va command +uses the remainder of the +.Nm +command line as its own arguments. +.It Va exec.poststart +Command(s) to run in the system environment after a jail is created, +and after any +.Va exec.start +commands have completed. +.It Va exec.prestop +Command(s) to run in the system environment before a jail is removed. +.It Va exec.stop +Command(s) to run in the prison environment before a jail is removed, +and after any +.Va exec.prestop +commands have completed. +A typical command to run is +.Dq sh /etc/rc.shutdown . +.It Va exec.poststop +Command(s) to run in the system environment after a jail is removed. +.It Va exec.clean +Run commands in a clean environment. +The environment is discarded except for +.Ev HOME , SHELL , TERM +and +.Ev USER . +.Ev HOME +and +.Ev SHELL +are set to the target login's default values. +.Ev USER +is set to the target login. +.Ev TERM +is imported from the current environment. +The environment variables from the login class capability database for the +target login are also set. +.It Va exec.jail_user +The user to run commands as, when running in the prison environment. +The default is to run the commands as the current user. +.It Va exec.system_jail_user +This boolean option looks for the +.Va exec.jail_user +in the system +.Xr passwd 5 +file, instead of in the prison's file. +.It Va exec.system_user +The user to run commands as, when running in the system environment. +The default is to run the commands as the current user. +.It Va exec.timeout +The maximum amount of time to wait for a command to complete. +If a command is still running after this many seconds have passed, +the jail not be created or removed. +.It Va exec.consolelog +A file to direct command output (stdout and stderr) to. +.It Va exec.fib +The FIB (routing table) to set when running commands inside the prison. +.It Va stop.timeout +The maximum amount of time to wait for a prison's processes to exit +after sending them a +.Dv SIGTERM +signal (which happens after the +.Va exec.stop commands have completed). +After this many seconds have passed, the prison will be removed, which +will kill any remaining processes. +If this is set to zero, no +.Dv SIGTERM +is sent and the prison is immediately removed. +The default is 10 seconds. +.It Va interface +A network interface to add the prison's IP addresses +.Va ( ip4.addr +and +.Va ip6.addr ) +to. +An alias for each address will be added to the interface before the +prison is created, and will be removed from the interface after the +prison is removed. +.It Op Va ip4.addr +In addition to the IP addresses that are passed to the kernel, and +interface and/or a netmask may also be specified, in the form +.Dq Ar interface Ns | Ns Ar ip-address Ns / Ns Ar netmask . +If an interface is given before the IP address, an alias for the address +will be added to that interface, as it is with the +.Va interface +parameter. If a netmask in either dotted-quad or CIDR form is given +after IP address, it will be used when adding the IP alias. +.It Op Va ip6.addr +In addition to the IP addresses that are passed to the kernel, +and interface and/or a prefix may also be specified, in the form +.Dq Ar interface Ns | Ns Ar ip-address Ns / Ns Ar prefix . +.It Va vnet.interface +A network interface to give to a vnet-enabled jail after is it created. +The interface will automatically be returned when the jail is removed. +.It Va ip_hostname +Resolve the +.Va host.hostname +parameter and add all IP addresses returned by the resolver +to the list of addresses +.Va ( ip4.addr +or +.Va ip6.addr ) +for this prison. +This may affect default address selection for outgoing IPv4 connections +of prisons. +The address first returned by the resolver for each address family +will be used as primary address. +.It Va mount +A filesystem to mount before creating the jail (and to unmount after +removing it), given as a single +.Xr fstab 5 +line. +.It Va mount.fstab +An +.Xr fstab 5 +format file containing filesystems to mount before creating a jail. +.It Va mount.devfs +Mount a +.Xr devfs +filesystem on the chrooted /dev directory, and apply the ruleset in the +.Va devfs_ruleset +parameter (or a default of ruleset 4: devfsrules_jail) +to restrict the devices visible inside the prison. +.It Va allow.dying +Allow making changes to a +.Va dying +jail. +.It Va depend +Specify a jail (or jails) that this jail depends on. +Any such jails must be fully created, up to the last +.Va exec.poststart +command, before any action will taken to create this jail. +When jails are removed the opposite is true: +this jail must be fully removed, up to the last +.Va exec.poststop +command, before the jail(s) it depends on are stopped. +.El +.Sh EXAMPLES Jails are typically set up using one of two philosophies: either to constrain a specific application (possibly running with privilege), or to create a .Dq "virtual system image" running a variety of daemons and services. In both cases, a fairly complete file system install of .Fx is required, so as to provide the necessary command line tools, daemons, libraries, application configuration files, etc. However, for a virtual server configuration, a fair amount of additional work is required so as to configure the .Dq boot process. This manual page documents the configuration steps necessary to support either of these steps, although the configuration steps may be refined based on local requirements. -.Sh EXAMPLES .Ss "Setting up a Jail Directory Tree" To set up a jail directory tree containing an entire .Fx distribution, the following .Xr sh 1 command script can be used: .Bd -literal D=/here/is/the/jail cd /usr/src mkdir -p $D make world DESTDIR=$D make distribution DESTDIR=$D -mount -t devfs devfs $D/dev .Ed .Pp -NOTE: It is important that only appropriate device nodes in devfs be -exposed to a jail; access to disk devices in the jail may permit processes -in the jail to bypass the jail sandboxing by modifying files outside of -the jail. -See -.Xr devfs 8 -for information on how to use devfs rules to limit access to entries -in the per-jail devfs. -A simple devfs ruleset for jails is available as ruleset #4 in -.Pa /etc/defaults/devfs.rules . -.Pp In many cases this example would put far more in the jail than needed. In the other extreme case a jail might contain only one file: the executable to be run in the jail. .Pp We recommend experimentation and caution that it is a lot easier to start with a .Dq fat jail and remove things until it stops working, than it is to start with a .Dq thin jail and add things until it works. .Ss "Setting Up a Jail" Do what was described in .Sx "Setting Up a Jail Directory Tree" to build the jail directory tree. For the sake of this example, we will assume you built it in -.Pa /data/jail/192.0.2.100 , -named for the jailed IP address. +.Pa /data/jail/testjail , +for a jail named +.Dq testjail . Substitute below as needed with your own directory, IP address, and hostname. .Ss "Setting up the Host Environment" First, you will want to set up your real system's environment to be .Dq jail-friendly . For consistency, we will refer to the parent box as the .Dq "host environment" , and to the jailed virtual machine as the .Dq "jail environment" . Since jail is implemented using IP aliases, one of the first things to do is to disable IP services on the host system that listen on all local IP addresses for a service. If a network service is present in the host environment that binds all available IP addresses rather than specific IP addresses, it may service requests sent to jail IP addresses if the jail did not bind the port. This means changing .Xr inetd 8 to only listen on the appropriate IP address, and so forth. Add the following to .Pa /etc/rc.conf in the host environment: .Bd -literal -offset indent sendmail_enable="NO" inetd_flags="-wW -a 192.0.2.23" rpcbind_enable="NO" .Ed .Pp .Li 192.0.2.23 is the native IP address for the host system, in this example. Daemons that run out of .Xr inetd 8 can be easily set to use only the specified host IP address. Other daemons will need to be manually configured\(emfor some this is possible through the .Xr rc.conf 5 flags entries; for others it is necessary to modify per-application configuration files, or to recompile the applications. The following frequently deployed services must have their individual configuration files modified to limit the application to listening to a specific IP address: .Pp To configure .Xr sshd 8 , it is necessary to modify .Pa /etc/ssh/sshd_config . .Pp To configure .Xr sendmail 8 , it is necessary to modify .Pa /etc/mail/sendmail.cf . .Pp For .Xr named 8 , it is necessary to modify .Pa /etc/namedb/named.conf . .Pp In addition, a number of services must be recompiled in order to run them in the host environment. This includes most applications providing services using .Xr rpc 3 , such as .Xr rpcbind 8 , .Xr nfsd 8 , and .Xr mountd 8 . In general, applications for which it is not possible to specify which IP address to bind should not be run in the host environment unless they should also service requests sent to jail IP addresses. Attempting to serve NFS from the host environment may also cause confusion, and cannot be easily reconfigured to use only specific IPs, as some NFS services are hosted directly from the kernel. Any third-party network software running in the host environment should also be checked and configured so that it does not bind all IP addresses, which would result in those services' also appearing to be offered by the jail environments. .Pp Once these daemons have been disabled or fixed in the host environment, it is best to reboot so that all daemons are in a known state, to reduce the potential for confusion later (such as finding that when you send mail to a jail, and its sendmail is down, the mail is delivered to the host, etc.). .Ss "Configuring the Jail" Start any jail for the first time without configuring the network interface so that you can clean it up a little and set up accounts. As with any machine (virtual or not) you will need to set a root password, time zone, etc. Some of these steps apply only if you intend to run a full virtual server inside the jail; others apply both for constraining a particular application or for running a virtual server. .Pp Start a shell in the jail: .Bd -literal -offset indent -jail -c path=/data/jail/192.0.2.100 host.hostname=testhostname \\ +jail -c path=/data/jail/testjail mount.devfs host.hostname=testhostname \\ ip4.addr=192.0.2.100 command=/bin/sh .Ed .Pp Assuming no errors, you will end up with a shell prompt within the jail. You can now run .Pa /usr/sbin/sysinstall and do the post-install configuration to set various configuration options, or perform these actions manually by editing .Pa /etc/rc.conf , etc. .Pp .Bl -bullet -offset indent -compact .It -Create an empty -.Pa /etc/fstab -to quell startup warnings about missing fstab (virtual server only) -.It -Disable the port mapper -.Pa ( /etc/rc.conf : -.Li rpcbind_enable="NO" ) -(virtual server only) -.It Configure .Pa /etc/resolv.conf so that name resolution within the jail will work correctly .It Run .Xr newaliases 1 to quell .Xr sendmail 8 warnings. .It -Disable interface configuration to quell startup warnings about -.Xr ifconfig 8 -.Pq Li network_interfaces="" -(virtual server only) -.It Set a root password, probably different from the real host system .It Set the timezone .It Add accounts for users in the jail environment .It Install any packages the environment requires .El .Pp You may also want to perform any package-specific configuration (web servers, SSH servers, etc), patch up .Pa /etc/syslog.conf so it logs as you would like, etc. If you are not using a virtual server, you may wish to modify .Xr syslogd 8 in the host environment to listen on the syslog socket in the jail environment; in this example, the syslog socket would be stored in -.Pa /data/jail/192.0.2.100/var/run/log . +.Pa /data/jail/testjail/var/run/log . .Pp Exit from the shell, and the jail will be shut down. .Ss "Starting the Jail" You are now ready to restart the jail and bring up the environment with all of its daemons and other programs. -If you are running a single application in the jail, substitute the -command used to start the application for -.Pa /etc/rc -in the examples below. +Create an entry for the jail in +.Pa /etc/jail.conf : +.Bd -literal -offset indent +testjail { + path = /tmp/jail/testjail; + mount.devfs; + host.hostname = testhostname; + ip4.addr = 192.0.2.100; + interface = ed0; + exec.start = "/bin/sh /etc/rc"; + exec.stop = "/bin/sh /etc/rc.shutdown"; +} +.Ed +.Pp To start a virtual server environment, .Pa /etc/rc -is run to launch various daemons and services. -To do this, first bring up the -virtual host interface, and then start the jail's -.Pa /etc/rc -script from within the jail. +is run to launch various daemons and services, and +.Pa /etc/rc.shutdown +is run to shut them down when the jail is removed. +If you are running a single application in the jail, +substitute the command used to start the application for +.Dq /bin/sh /etc/rc ; +there may be some script available to cleanly shut down the application, +or it may be sufficient to go without a stop command, and have +.Nm +send +.Dv SIGTERM +to the application. +.Pp +Start the jail by running: .Bd -literal -offset indent -ifconfig ed0 inet alias 192.0.2.100/32 -mount -t procfs proc /data/jail/192.0.2.100/proc -jail -c path=/data/jail/192.0.2.100 host.hostname=testhostname \\ - ip4.addr=192.0.2.100 command=/bin/sh /etc/rc +jail -c testjail .Ed .Pp -A few warnings will be produced, because most -.Xr sysctl 8 -configuration variables cannot be set from within the jail, as they are -global across all jails and the host environment. -However, it should all -work properly. +A few warnings may be produced; however, it should all work properly. You should be able to see .Xr inetd 8 , .Xr syslogd 8 , and other processes running within the jail using .Xr ps 1 , with the .Ql J flag appearing beside jailed processes. To see an active list of jails, use the .Xr jls 8 utility. You should also be able to .Xr telnet 1 to the hostname or IP address of the jailed environment, and log in using the accounts you created previously. .Pp It is possible to have jails started at boot time. Please refer to the .Dq jail_* variables in .Xr rc.conf 5 for more information. -The -.Xr rc 8 -jail script provides a flexible system to start/stop jails: -.Bd -literal -/etc/rc.d/jail start -/etc/rc.d/jail stop -/etc/rc.d/jail start myjail -/etc/rc.d/jail stop myjail -.Ed .Ss "Managing the Jail" Normal machine shutdown commands, such as .Xr halt 8 , .Xr reboot 8 , and .Xr shutdown 8 , cannot be used successfully within the jail. -To kill all processes in a -jail, you may log into the jail and, as root, use one of the following -commands, depending on what you want to accomplish: +To kill all processes from within a jail, you may use one of the +following commands, depending on what you want to accomplish: .Bd -literal -offset indent kill -TERM -1 kill -KILL -1 .Ed .Pp This will send the .Dv SIGTERM or .Dv SIGKILL -signals to all processes in the jail from within the jail. +signals to all processes in the jail - be careful not to run this from +the host environment! +Once all of the jail's processes have died, unless the jail was created +with the +.Va persist +parameter, the jail will be removed. Depending on the intended use of the jail, you may also want to run .Pa /etc/rc.shutdown from within the jail. -To kill processes from outside the jail, use the -.Xr jexec 8 -utility in conjunction with the one of the -.Xr kill 1 -commands above. -You may also remove the jail with +.Pp +To shut down the jail from the outside, simply remove it with .Nm .Ar -r , -which will killall the jail's processes with -.Dv SIGKILL . +which will run any commands specified by +.Va exec.stop , +and then send +.Dv SIGTERM +and eventually +.Dv SIGKILL +to any remaining jailed processes. .Pp The .Pa /proc/ Ns Ar pid Ns Pa /status file contains, as its last field, the name of the jail in which the process runs, or .Dq Li - to indicate that the process is not running within a jail. The .Xr ps 1 command also shows a .Ql J flag for processes in a jail. .Pp You can also list/kill processes based on their jail ID. To show processes and their jail ID, use the following command: .Pp .Dl "ps ax -o pid,jid,args" .Pp To show and then kill processes in jail number 3 use the following commands: .Bd -literal -offset indent pgrep -lfj 3 pkill -j 3 .Ed or: .Pp .Dl "killall -j 3" .Ss "Jails and File Systems" It is not possible to .Xr mount 8 or .Xr umount 8 any file system inside a jail unless the file system is marked jail-friendly, the jail's .Va allow.mount parameter is set and the jail's .Va enforce_statfs parameter is lower than 2. .Pp Multiple jails sharing the same file system can influence each other. For example a user in one jail can fill the file system also leaving no space for processes in the other jail. Trying to use .Xr quota 1 to prevent this will not work either as the file system quotas are not aware of jails but only look at the user and group IDs. This means the same user ID in two jails share the same file system quota. One would need to use one file system per jail to make this work. .Ss "Sysctl MIB Entries" The read-only entry .Va security.jail.jailed can be used to determine if a process is running inside a jail (value is one) or not (value is zero). .Pp The variable .Va security.jail.max_af_ips determines how may address per address family a prison may have. The default is 255. .Pp Some MIB variables have per-jail settings. Changes to these variables by a jailed process do not effect the host environment, only the jail environment. These variables are .Va kern.securelevel , .Va kern.hostname , .Va kern.domainname , .Va kern.hostid , and .Va kern.hostuuid . .Ss "Hierarchical Jails" By setting a jail's .Va children.max parameter, processes within a jail may be able to create jails of their own. These child jails are kept in a hierarchy, with jails only able to see and/or modify the jails they created (or those jails' children). Each jail has a read-only .Va parent parameter, containing the .Va jid of the jail that created it; a .Va jid of 0 indicates the jail is a child of the current jail (or is a top-level jail if the current process isn't jailed). .Pp Jailed processes are not allowed to confer greater permissions than they themselves are given, e.g. if a jail is created with .Va allow.nomount , it is not able to create a jail with .Va allow.mount set. Similarly, such restrictions as .Va ip4.addr and .Va securelevel may not be bypassed in child jails. .Pp A child jail may in turn create its own child jails if its own .Va children.max parameter is set (remember it is zero by default). These jails are visible to and can be modified by their parent and all ancestors. .Pp Jail names reflect this hierarchy, with a full name being an MIB-type string separated by dots. For example, if a base system process creates a jail .Dq foo , and a process under that jail creates another jail .Dq bar , then the second jail will be seen as .Dq foo.bar in the base system (though it is only seen as .Dq bar to any processes inside jail .Dq foo ) . Jids on the other hand exist in a single space, and each jail must have a unique jid. .Pp Like the names, a child jail's .Va path -is relative to its creator's own +appears relative to its creator's own .Va path . This is by virtue of the child jail being created in the chrooted environment of the first jail. .Sh SEE ALSO .Xr killall 1 , .Xr lsvfs 1 , .Xr newaliases 1 , .Xr pgrep 1 , .Xr pkill 1 , .Xr ps 1 , .Xr quota 1 , -.Xr chroot 2 , .Xr jail_set 2 , -.Xr jail_attach 2 , +.Xr jail.conf 5 , .Xr procfs 5 , .Xr rc.conf 5 , .Xr sysctl.conf 5 , +.Xr chroot 8 , .Xr devfs 8 , .Xr halt 8 , .Xr inetd 8 , .Xr jexec 8 , .Xr jls 8 , .Xr mount 8 , .Xr named 8 , .Xr reboot 8 , .Xr rpcbind 8 , .Xr sendmail 8 , .Xr shutdown 8 , .Xr sysctl 8 , .Xr syslogd 8 , .Xr umount 8 .Sh HISTORY The .Nm utility appeared in .Fx 4.0 . Hierarchical/extensible jails were introduced in .Fx 8.0 . +The configuration file was introduced in +.Fx 9.1 . .Sh AUTHORS .An -nosplit The jail feature was written by .An Poul-Henning Kamp for R&D Associates .Pa http://www.rndassociates.com/ who contributed it to .Fx . .Pp .An Robert Watson wrote the extended documentation, found a few bugs, added a few new features, and cleaned up the userland jail environment. .Pp .An Bjoern A. Zeeb added multi-IP jail support for IPv4 and IPv6 based on a patch originally done by .An Pawel Jakub Dawidek for IPv4. .Pp .An James Gritton -added the extensible jail parameters and hierarchical jails. +added the extensible jail parameters, hierarchical jails, +and the configuration file. .Sh BUGS -Jail currently lacks the ability to allow access to -specific jail information via -.Xr ps 1 -as opposed to -.Xr procfs 5 . -Similarly, it might be a good idea to add an +It might be a good idea to add an address alias flag such that daemons listening on all IPs .Pq Dv INADDR_ANY will not bind on that address, which would facilitate building a safe host environment such that host daemons do not impose on services offered from within jails. Currently, the simplest answer is to minimize services offered on the host, possibly limiting it to services offered from .Xr inetd 8 which is easily configurable. .Sh NOTES Great care should be taken when managing directories visible within the jail. For example, if a jailed process has its current working directory set to a directory that is moved out of the jail's chroot, then the process may gain access to the file space outside of the jail. It is recommended that directories always be copied, rather than moved, out of a jail. Index: stable/9/usr.sbin/jail/jail.c =================================================================== --- stable/9/usr.sbin/jail/jail.c (revision 235838) +++ stable/9/usr.sbin/jail/jail.c (revision 235839) @@ -1,575 +1,989 @@ /*- * Copyright (c) 1999 Poul-Henning Kamp. - * Copyright (c) 2009 James Gritton + * Copyright (c) 2009-2012 James Gritton * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); -#include -#include +#include +#include #include #include #include #include -#include #include #include -#include -#include -#include -#include -#include -#include +#include #include #include #include #include -static struct jailparam *params; -static char **param_values; -static int nparams; +#include "jailp.h" -#ifdef INET6 -static int ip6_ok; -static char *ip6_addr; -#endif -#ifdef INET -static int ip4_ok; -static char *ip4_addr; -#endif +#define JP_RDTUN(jp) (((jp)->jp_ctltype & CTLFLAG_RDTUN) == CTLFLAG_RDTUN) -#if defined(INET6) || defined(INET) -static void add_ip_addr(char **addrp, char *newaddr); -#endif -#ifdef INET6 -static void add_ip_addr46(char *newaddr); -#endif -static void add_ip_addrinfo(int ai_flags, char *value); +struct permspec { + const char *name; + enum intparam ipnum; + int rev; +}; + +const char *cfname; +int note_remove; +int verbose; + +static void clear_persist(struct cfjail *j); +static int update_jail(struct cfjail *j); +static int rdtun_params(struct cfjail *j, int dofail); +static void running_jid(struct cfjail *j, int dflag); +static void jail_quoted_warnx(const struct cfjail *j, const char *name_msg, + const char *noname_msg); +static int jailparam_set_note(const struct cfjail *j, struct jailparam *jp, + unsigned njp, int flags); +static void print_jail(FILE *fp, struct cfjail *j, int oldcl); +static void print_param(FILE *fp, const struct cfparam *p, int sep, int doname); static void quoted_print(FILE *fp, char *str); -static void set_param(const char *name, char *value); static void usage(void); -static const char *perm_sysctl[][3] = { - { "security.jail.set_hostname_allowed", - "allow.noset_hostname", "allow.set_hostname" }, - { "security.jail.sysvipc_allowed", - "allow.nosysvipc", "allow.sysvipc" }, - { "security.jail.allow_raw_sockets", - "allow.noraw_sockets", "allow.raw_sockets" }, - { "security.jail.chflags_allowed", - "allow.nochflags", "allow.chflags" }, - { "security.jail.mount_allowed", - "allow.nomount", "allow.mount" }, - { "security.jail.socket_unixiproute_only", - "allow.socket_af", "allow.nosocket_af" }, +static struct permspec perm_sysctl[] = { + { "security.jail.set_hostname_allowed", KP_ALLOW_SET_HOSTNAME, 0 }, + { "security.jail.sysvipc_allowed", KP_ALLOW_SYSVIPC, 0 }, + { "security.jail.allow_raw_sockets", KP_ALLOW_RAW_SOCKETS, 0 }, + { "security.jail.chflags_allowed", KP_ALLOW_CHFLAGS, 0 }, + { "security.jail.mount_allowed", KP_ALLOW_MOUNT, 0 }, + { "security.jail.socket_unixiproute_only", KP_ALLOW_SOCKET_AF, 1 }, }; -extern char **environ; +static const enum intparam startcommands[] = { + IP__NULL, +#ifdef INET + IP__IP4_IFADDR, +#endif +#ifdef INET6 + IP__IP6_IFADDR, +#endif + IP_MOUNT, + IP__MOUNT_FROM_FSTAB, + IP_MOUNT_DEVFS, + IP_EXEC_PRESTART, + IP__OP, + IP_VNET_INTERFACE, + IP_EXEC_START, + IP_COMMAND, + IP_EXEC_POSTSTART, + IP__NULL +}; -#define GET_USER_INFO do { \ - pwd = getpwnam(username); \ - if (pwd == NULL) { \ - if (errno) \ - err(1, "getpwnam: %s", username); \ - else \ - errx(1, "%s: no such user", username); \ - } \ - lcap = login_getpwclass(pwd); \ - if (lcap == NULL) \ - err(1, "getpwclass: %s", username); \ - ngroups = ngroups_max; \ - if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \ - err(1, "getgrouplist: %s", username); \ -} while (0) +static const enum intparam stopcommands[] = { + IP__NULL, + IP_EXEC_PRESTOP, + IP_EXEC_STOP, + IP_STOP_TIMEOUT, + IP__OP, + IP_EXEC_POSTSTOP, + IP_MOUNT_DEVFS, + IP__MOUNT_FROM_FSTAB, + IP_MOUNT, +#ifdef INET6 + IP__IP6_IFADDR, +#endif +#ifdef INET + IP__IP4_IFADDR, +#endif + IP__NULL +}; int main(int argc, char **argv) { - login_cap_t *lcap = NULL; - struct passwd *pwd = NULL; - gid_t *groups; + struct stat st; + FILE *jfp; + struct cfjail *j; + char *JidFile; size_t sysvallen; - int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval; - int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag; - long ngroups_max; - unsigned pi; - char *jailname, *securelevel, *username, *JidFile; + unsigned op, pi; + int ch, docf, error, i, oldcl, sysval; + int dflag, iflag, Rflag; char enforce_statfs[4]; - static char *cleanenv; - const char *shell, *p = NULL; - FILE *fp; +#if defined(INET) || defined(INET6) + char *cs, *ncs; +#endif +#if defined(INET) && defined(INET6) + struct in6_addr addr6; +#endif - hflag = iflag = Jflag = lflag = rflag = uflag = Uflag = - jail_set_flags = 0; - cmdarg = jid = -1; - jailname = securelevel = username = JidFile = cleanenv = NULL; - fp = NULL; + op = 0; + dflag = iflag = Rflag = 0; + docf = 1; + cfname = CONF_FILE; + JidFile = NULL; - ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; - if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) - err(1, "malloc"); - - while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) { + while ((ch = getopt(argc, argv, "cdf:hiJ:lmn:p:qrRs:U:v")) != -1) { switch (ch) { + case 'c': + op |= JF_START; + break; case 'd': - jail_set_flags |= JAIL_DYING; + dflag = 1; break; + case 'f': + cfname = optarg; + break; case 'h': - hflag = 1; +#if defined(INET) || defined(INET6) + add_param(NULL, NULL, IP_IP_HOSTNAME, NULL); +#endif + docf = 0; break; case 'i': iflag = 1; + verbose = -1; break; case 'J': JidFile = optarg; - Jflag = 1; break; + case 'l': + add_param(NULL, NULL, IP_EXEC_CLEAN, NULL); + docf = 0; + break; + case 'm': + op |= JF_SET; + break; case 'n': - jailname = optarg; + add_param(NULL, NULL, KP_NAME, optarg); + docf = 0; break; - case 's': - securelevel = optarg; + case 'p': + paralimit = strtol(optarg, NULL, 10); + if (paralimit == 0) + paralimit = -1; break; - case 'u': - username = optarg; - uflag = 1; + case 'q': + verbose = -1; break; - case 'U': - username = optarg; - Uflag = 1; + case 'r': + op |= JF_STOP; break; - case 'l': - lflag = 1; + case 'R': + op |= JF_STOP; + Rflag = 1; break; - case 'c': - jail_set_flags |= JAIL_CREATE; + case 's': + add_param(NULL, NULL, KP_SECURELEVEL, optarg); + docf = 0; break; - case 'm': - jail_set_flags |= JAIL_UPDATE; + case 'u': + add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg); + add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER, NULL); + docf = 0; break; - case 'r': - jid = jail_getid(optarg); - if (jid < 0) - errx(1, "%s", jail_errmsg); - rflag = 1; + case 'U': + add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg); + add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER, + "false"); + docf = 0; break; + case 'v': + verbose = 1; + break; default: usage(); } } argc -= optind; argv += optind; - if (rflag) { - if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag) - usage(); - if (jail_remove(jid) < 0) - err(1, "jail_remove"); - exit (0); - } - if (argc == 0) - usage(); - if (uflag && Uflag) - usage(); - if (lflag && username == NULL) - usage(); - if (uflag) - GET_USER_INFO; -#ifdef INET6 - ip6_ok = feature_present("inet6"); + /* Find out which of the four command line styles this is. */ + oldcl = 0; + if (!op) { + /* Old-style command line with four fixed parameters */ + if (argc < 4 || argv[0][0] != '/') + usage(); + op = JF_START; + docf = 0; + oldcl = 1; + add_param(NULL, NULL, KP_PATH, argv[0]); + add_param(NULL, NULL, KP_HOST_HOSTNAME, argv[1]); +#if defined(INET) || defined(INET6) + if (argv[2][0] != '\0') { + for (cs = argv[2];; cs = ncs + 1) { + ncs = strchr(cs, ','); + if (ncs) + *ncs = '\0'; + add_param(NULL, NULL, +#if defined(INET) && defined(INET6) + inet_pton(AF_INET6, cs, &addr6) == 1 + ? KP_IP6_ADDR : KP_IP4_ADDR, +#elif defined(INET) + KP_IP4_ADDR, +#elif defined(INET6) + KP_IP6_ADDR, #endif -#ifdef INET - ip4_ok = feature_present("inet"); -#endif - - if (jailname) - set_param("name", jailname); - if (securelevel) - set_param("securelevel", securelevel); - if (jail_set_flags) { - for (i = 0; i < argc; i++) { - if (!strncmp(argv[i], "command=", 8)) { - cmdarg = i; - argv[cmdarg] += 8; - jail_set_flags |= JAIL_ATTACH; - break; - } - if (hflag) { -#ifdef INET - if (!strncmp(argv[i], "ip4.addr=", 9)) { - add_ip_addr(&ip4_addr, argv[i] + 9); + cs); + if (!ncs) break; - } -#endif -#ifdef INET6 - if (!strncmp(argv[i], "ip6.addr=", 9)) { - add_ip_addr(&ip6_addr, argv[i] + 9); - break; - } -#endif - if (!strncmp(argv[i], "host.hostname=", 14)) - add_ip_addrinfo(0, argv[i] + 14); } - set_param(NULL, argv[i]); } - } else { - if (argc < 4 || argv[0][0] != '/') - errx(1, "%s\n%s", - "no -c or -m, so this must be an old-style command.", - "But it doesn't look like one."); - set_param("path", argv[0]); - set_param("host.hostname", argv[1]); - if (hflag) - add_ip_addrinfo(0, argv[1]); -#if defined(INET6) || defined(INET) - if (argv[2][0] != '\0') -#ifdef INET6 - add_ip_addr46(argv[2]); -#else - add_ip_addr(&ip4_addr, argv[2]); #endif -#endif - cmdarg = 3; - /* Emulate the defaults from security.jail.* sysctls */ + for (i = 3; i < argc; i++) + add_param(NULL, NULL, IP_COMMAND, argv[i]); + /* Emulate the defaults from security.jail.* sysctls. */ sysvallen = sizeof(sysval); if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen, NULL, 0) == 0 && sysval == 0) { for (pi = 0; pi < sizeof(perm_sysctl) / sizeof(perm_sysctl[0]); pi++) { sysvallen = sizeof(sysval); - if (sysctlbyname(perm_sysctl[pi][0], + if (sysctlbyname(perm_sysctl[pi].name, &sysval, &sysvallen, NULL, 0) == 0) - set_param(perm_sysctl[pi] - [sysval ? 2 : 1], NULL); + add_param(NULL, NULL, + perm_sysctl[pi].ipnum, + (sysval ? 1 : 0) ^ + perm_sysctl[pi].rev + ? NULL : "false"); } sysvallen = sizeof(sysval); if (sysctlbyname("security.jail.enforce_statfs", &sysval, &sysvallen, NULL, 0) == 0) { snprintf(enforce_statfs, sizeof(enforce_statfs), "%d", sysval); - set_param("enforce_statfs", enforce_statfs); + add_param(NULL, NULL, KP_ENFORCE_STATFS, + enforce_statfs); } } + } else if (op == JF_STOP) { + /* Jail remove, perhaps using the config file */ + if (!docf || argc == 0) + usage(); + if (!Rflag) + for (i = 0; i < argc; i++) + if (strchr(argv[i], '=')) + usage(); + if ((docf = !Rflag && + (!strcmp(cfname, "-") || stat(cfname, &st) == 0))) + load_config(); + note_remove = docf || argc > 1 || wild_jail_name(argv[0]); + } else if (argc > 1 || (argc == 1 && strchr(argv[0], '='))) { + /* Single jail specified on the command line */ + if (Rflag) + usage(); + docf = 0; + for (i = 0; i < argc; i++) { + if (!strncmp(argv[i], "command", 7) && + (argv[i][7] == '\0' || argv[i][7] == '=')) { + if (argv[i][7] == '=') + add_param(NULL, NULL, IP_COMMAND, + argv[i] + 8); + for (i++; i < argc; i++) + add_param(NULL, NULL, IP_COMMAND, + argv[i]); + break; + } + add_param(NULL, NULL, 0, argv[i]); + } + } else { + /* From the config file, perhaps with a specified jail */ + if (Rflag || !docf) + usage(); + load_config(); } -#ifdef INET - if (ip4_addr != NULL) - set_param("ip4.addr", ip4_addr); -#endif -#ifdef INET6 - if (ip6_addr != NULL) - set_param("ip6.addr", ip6_addr); -#endif - if (Jflag) { - fp = fopen(JidFile, "w"); - if (fp == NULL) - errx(1, "Could not create JidFile: %s", JidFile); + /* Find out which jails will be run. */ + dep_setup(docf); + error = 0; + if (op == JF_STOP) { + for (i = 0; i < argc; i++) + if (start_state(argv[i], docf, op, Rflag) < 0) + error = 1; + } else { + if (start_state(argv[0], docf, op, 0) < 0) + exit(1); } - jid = jailparam_set(params, nparams, - jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH); - if (jid < 0) - errx(1, "%s", jail_errmsg); - if (iflag) { - printf("%d\n", jid); - fflush(stdout); + + jfp = NULL; + if (JidFile != NULL) { + jfp = fopen(JidFile, "w"); + if (jfp == NULL) + err(1, "open %s", JidFile); + setlinebuf(jfp); } - if (Jflag) { - if (jail_set_flags) { - fprintf(fp, "jid=%d", jid); - for (i = 0; i < nparams; i++) - if (strcmp(params[i].jp_name, "jid")) { - fprintf(fp, " %s", - (char *)params[i].jp_name); - if (param_values[i]) { - putc('=', fp); - quoted_print(fp, - param_values[i]); - } + setlinebuf(stdout); + + /* + * The main loop: Get an available jail and perform the required + * operation on it. When that is done, the jail may be finished, + * or it may go back for the next step. + */ + while ((j = next_jail())) + { + if (j->flags & JF_FAILED) { + error = 1; + if (j->comparam == NULL) { + dep_done(j, 0); + continue; + } + } + if (!(j->flags & JF_PARAMS)) + { + j->flags |= JF_PARAMS; + if (dflag) + add_param(j, NULL, IP_ALLOW_DYING, NULL); + if (check_intparams(j) < 0) + continue; + if ((j->flags & (JF_START | JF_SET)) && + import_params(j) < 0) + continue; + } + if (!j->jid) + running_jid(j, + (j->flags & (JF_SET | JF_DEPEND)) == JF_SET + ? dflag || bool_param(j->intparams[IP_ALLOW_DYING]) + : 0); + if (finish_command(j)) + continue; + + switch (j->flags & JF_OP_MASK) { + /* + * These operations just turn into a different op + * depending on the jail's current status. + */ + case JF_START_SET: + j->flags = j->jid < 0 ? JF_START : JF_SET; + break; + case JF_SET_RESTART: + if (j->jid < 0) { + jail_quoted_warnx(j, "not found", + "no jail specified"); + failed(j); + continue; + } + j->flags = rdtun_params(j, 0) ? JF_RESTART : JF_SET; + if (j->flags == JF_RESTART) + dep_reset(j); + break; + case JF_START_SET_RESTART: + j->flags = j->jid < 0 ? JF_START + : rdtun_params(j, 0) ? JF_RESTART : JF_SET; + if (j->flags == JF_RESTART) + dep_reset(j); + } + + switch (j->flags & JF_OP_MASK) { + case JF_START: + if (j->comparam == NULL) { + if (j->jid > 0 && + !(j->flags & (JF_DEPEND | JF_WILD))) { + jail_quoted_warnx(j, "already exists", + NULL); + failed(j); + continue; } - fprintf(fp, "\n"); - } else { - for (i = 0; i < nparams; i++) - if (!strcmp(params[i].jp_name, "path")) - break; -#if defined(INET6) && defined(INET) - fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n", - jid, i < nparams - ? (char *)params[i].jp_value : argv[0], - argv[1], ip4_addr ? ip4_addr : "", - ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0] - ? "," : "", ip6_addr ? ip6_addr : "", argv[3]); -#elif defined(INET6) - fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", - jid, i < nparams - ? (char *)params[i].jp_value : argv[0], - argv[1], ip6_addr ? ip6_addr : "", argv[3]); -#elif defined(INET) - fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", - jid, i < nparams - ? (char *)params[i].jp_value : argv[0], - argv[1], ip4_addr ? ip4_addr : "", argv[3]); -#endif + if (dep_check(j)) + continue; + if (j->jid > 0) + goto jail_create_done; + j->comparam = startcommands; + j->comstring = NULL; + } + if (next_command(j)) + continue; + jail_create_done: + clear_persist(j); + if (iflag) + printf("%d\n", j->jid); + if (jfp != NULL) + print_jail(jfp, j, oldcl); + dep_done(j, 0); + break; + + case JF_SET: + if (j->jid < 0 && !(j->flags & JF_DEPEND)) { + jail_quoted_warnx(j, "not found", + "no jail specified"); + failed(j); + continue; + } + if (dep_check(j)) + continue; + if (!(j->flags & JF_DEPEND)) { + if (rdtun_params(j, 1) < 0 || + update_jail(j) < 0) + continue; + if (verbose >= 0 && (j->name || verbose > 0)) + jail_note(j, "updated\n"); + } + dep_done(j, 0); + break; + + case JF_STOP: + case JF_RESTART: + if (j->comparam == NULL) { + if (dep_check(j)) + continue; + if (j->jid < 0) { + if (!(j->flags & (JF_DEPEND | JF_WILD)) + && verbose >= 0) + jail_quoted_warnx(j, + "not found", NULL); + goto jail_remove_done; + } + j->comparam = stopcommands; + j->comstring = NULL; + } else if ((j->flags & JF_FAILED) && j->jid > 0) + goto jail_remove_done; + if (next_command(j)) + continue; + jail_remove_done: + dep_done(j, 0); + if ((j->flags & (JF_START | JF_FAILED)) == JF_START) { + j->comparam = NULL; + j->flags &= ~JF_STOP; + dep_reset(j); + requeue(j, j->ndeps ? &depend : &ready); + } + break; } - (void)fclose(fp); } - if (cmdarg < 0) - exit(0); - if (username != NULL) { - if (Uflag) - GET_USER_INFO; - if (lflag) { - p = getenv("TERM"); - environ = &cleanenv; + + if (jfp != NULL) + fclose(jfp); + exit(error); +} + +/* + * Mark a jail's failure for future handling. + */ +void +failed(struct cfjail *j) +{ + j->flags |= JF_FAILED; + TAILQ_REMOVE(j->queue, j, tq); + TAILQ_INSERT_HEAD(&ready, j, tq); + j->queue = &ready; +} + +/* + * Exit slightly more gracefully when out of memory. + */ +void * +emalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) + err(1, "malloc"); + return p; +} + +void * +erealloc(void *ptr, size_t size) +{ + void *p; + + p = realloc(ptr, size); + if (!p) + err(1, "malloc"); + return p; +} + +char * +estrdup(const char *str) +{ + char *ns; + + ns = strdup(str); + if (!ns) + err(1, "malloc"); + return ns; +} + +/* + * Print a message including an optional jail name. + */ +void +jail_note(const struct cfjail *j, const char *fmt, ...) +{ + va_list ap, tap; + char *cs; + size_t len; + + va_start(ap, fmt); + va_copy(tap, ap); + len = vsnprintf(NULL, 0, fmt, tap); + va_end(tap); + cs = alloca(len + 1); + (void)vsnprintf(cs, len + 1, fmt, ap); + va_end(ap); + if (j->name) + printf("%s: %s", j->name, cs); + else + printf("%s", cs); +} + +/* + * Print a warning message including an optional jail name. + */ +void +jail_warnx(const struct cfjail *j, const char *fmt, ...) +{ + va_list ap, tap; + char *cs; + size_t len; + + va_start(ap, fmt); + va_copy(tap, ap); + len = vsnprintf(NULL, 0, fmt, tap); + va_end(tap); + cs = alloca(len + 1); + (void)vsnprintf(cs, len + 1, fmt, ap); + va_end(ap); + if (j->name) + warnx("%s: %s", j->name, cs); + else + warnx("%s", cs); +} + +/* + * Create a new jail. + */ +int +create_jail(struct cfjail *j) +{ + struct iovec jiov[4]; + struct stat st; + struct jailparam *jp, *setparams, *setparams2, *sjp; + const char *path; + int dopersist, ns, jid, dying, didfail; + + /* + * Check the jail's path, with a better error message than jail_set + * gives. + */ + if ((path = string_param(j->intparams[KP_PATH]))) { + if (j->name != NULL && path[0] != '/') { + jail_warnx(j, "path %s: not an absolute pathname", + path); + return -1; } - if (setgroups(ngroups, groups) != 0) - err(1, "setgroups"); - if (setgid(pwd->pw_gid) != 0) - err(1, "setgid"); - if (setusercontext(lcap, pwd, pwd->pw_uid, - LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0) - err(1, "setusercontext"); - login_close(lcap); + if (stat(path, &st) < 0) { + jail_warnx(j, "path %s: %s", path, strerror(errno)); + return -1; + } + if (!S_ISDIR(st.st_mode)) { + jail_warnx(j, "path %s: %s", path, strerror(ENOTDIR)); + return -1; + } } - if (lflag) { - if (*pwd->pw_shell) - shell = pwd->pw_shell; - else - shell = _PATH_BSHELL; - if (chdir(pwd->pw_dir) < 0) - errx(1, "no home directory"); - setenv("HOME", pwd->pw_dir, 1); - setenv("SHELL", shell, 1); - setenv("USER", pwd->pw_name, 1); - if (p) - setenv("TERM", p, 1); + + /* + * Copy all the parameters, except that "persist" is always set when + * there are commands to run later. + */ + dopersist = !bool_param(j->intparams[KP_PERSIST]) && + (j->intparams[IP_EXEC_START] || j->intparams[IP_COMMAND] || + j->intparams[IP_EXEC_POSTSTART]); + sjp = setparams = + alloca((j->njp + dopersist) * sizeof(struct jailparam)); + if (dopersist && jailparam_init(sjp++, "persist") < 0) { + jail_warnx(j, "%s", jail_errmsg); + return -1; } - execvp(argv[cmdarg], argv + cmdarg); - err(1, "execvp: %s", argv[cmdarg]); + for (jp = j->jp; jp < j->jp + j->njp; jp++) + if (!dopersist || !equalopts(jp->jp_name, "persist")) + *sjp++ = *jp; + ns = sjp - setparams; + + didfail = 0; + j->jid = jailparam_set_note(j, setparams, ns, JAIL_CREATE); + if (j->jid < 0 && errno == EEXIST && + bool_param(j->intparams[IP_ALLOW_DYING]) && + int_param(j->intparams[KP_JID], &jid) && jid != 0) { + /* + * The jail already exists, but may be dying. + * Make sure it is, in which case an update is appropriate. + */ + *(const void **)&jiov[0].iov_base = "jid"; + jiov[0].iov_len = sizeof("jid"); + jiov[1].iov_base = &jid; + jiov[1].iov_len = sizeof(jid); + *(const void **)&jiov[2].iov_base = "dying"; + jiov[2].iov_len = sizeof("dying"); + jiov[3].iov_base = &dying; + jiov[3].iov_len = sizeof(dying); + if (jail_get(jiov, 4, JAIL_DYING) < 0) { + /* + * It could be that the jail just barely finished + * dying, or it could be that the jid never existed + * but the name does. In either case, another try + * at creating the jail should do the right thing. + */ + if (errno == ENOENT) + j->jid = jailparam_set_note(j, setparams, ns, + JAIL_CREATE); + } else if (dying) { + j->jid = jid; + if (rdtun_params(j, 1) < 0) { + j->jid = -1; + didfail = 1; + } else { + sjp = setparams2 = alloca((j->njp + dopersist) * + sizeof(struct jailparam)); + for (jp = setparams; jp < setparams + ns; jp++) + if (!JP_RDTUN(jp) || + !strcmp(jp->jp_name, "jid")) + *sjp++ = *jp; + j->jid = jailparam_set_note(j, setparams2, + sjp - setparams2, JAIL_UPDATE | JAIL_DYING); + /* + * Again, perhaps the jail just finished dying. + */ + if (j->jid < 0 && errno == ENOENT) + j->jid = jailparam_set_note(j, + setparams, ns, JAIL_CREATE); + } + } + } + if (j->jid < 0 && !didfail) { + jail_warnx(j, "%s", jail_errmsg); + failed(j); + } + if (dopersist) { + jailparam_free(setparams, 1); + if (j->jid > 0) + j->flags |= JF_PERSIST; + } + return j->jid; } -#if defined(INET6) || defined(INET) +/* + * Remove a temporarily set "persist" parameter. + */ static void -add_ip_addr(char **addrp, char *value) +clear_persist(struct cfjail *j) { - int addrlen; - char *addr; + struct iovec jiov[4]; + int jid; - if (!*addrp) { - *addrp = strdup(value); - if (!*addrp) - err(1, "malloc"); - } else if (value[0]) { - addrlen = strlen(*addrp) + strlen(value) + 2; - addr = malloc(addrlen); - if (!addr) - err(1, "malloc"); - snprintf(addr, addrlen, "%s,%s", *addrp, value); - free(*addrp); - *addrp = addr; + if (!(j->flags & JF_PERSIST)) + return; + j->flags &= ~JF_PERSIST; + *(const void **)&jiov[0].iov_base = "jid"; + jiov[0].iov_len = sizeof("jid"); + jiov[1].iov_base = &j->jid; + jiov[1].iov_len = sizeof(j->jid); + *(const void **)&jiov[2].iov_base = "nopersist"; + jiov[2].iov_len = sizeof("nopersist"); + jiov[3].iov_base = NULL; + jiov[3].iov_len = 0; + jid = jail_set(jiov, 4, JAIL_UPDATE); + if (verbose > 0) + jail_note(j, "jail_set(JAIL_UPDATE) jid=%d nopersist%s%s\n", + j->jid, jid < 0 ? ": " : "", + jid < 0 ? strerror(errno) : ""); +} + +/* + * Set a jail's parameters. + */ +static int +update_jail(struct cfjail *j) +{ + struct jailparam *jp, *setparams, *sjp; + int ns, jid; + + ns = 0; + for (jp = j->jp; jp < j->jp + j->njp; jp++) + if (!JP_RDTUN(jp)) + ns++; + if (ns == 0) + return 0; + sjp = setparams = alloca(++ns * sizeof(struct jailparam)); + if (jailparam_init(sjp, "jid") < 0 || + jailparam_import_raw(sjp, &j->jid, sizeof j->jid) < 0) { + jail_warnx(j, "%s", jail_errmsg); + failed(j); + return -1; } + for (jp = j->jp; jp < j->jp + j->njp; jp++) + if (!JP_RDTUN(jp)) + *++sjp = *jp; + + jid = jailparam_set_note(j, setparams, ns, + bool_param(j->intparams[IP_ALLOW_DYING]) + ? JAIL_UPDATE | JAIL_DYING : JAIL_UPDATE); + if (jid < 0) { + jail_warnx(j, "%s", jail_errmsg); + failed(j); + } + jailparam_free(setparams, 1); + return jid; } -#endif -#ifdef INET6 +/* + * Return if a jail set would change any create-only parameters. + */ +static int +rdtun_params(struct cfjail *j, int dofail) +{ + struct jailparam *jp, *rtparams, *rtjp; + int nrt, rval; + + if (j->flags & JF_RDTUN) + return 0; + j->flags |= JF_RDTUN; + nrt = 0; + for (jp = j->jp; jp < j->jp + j->njp; jp++) + if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) + nrt++; + if (nrt == 0) + return 0; + rtjp = rtparams = alloca(++nrt * sizeof(struct jailparam)); + if (jailparam_init(rtjp, "jid") < 0 || + jailparam_import_raw(rtjp, &j->jid, sizeof j->jid) < 0) { + jail_warnx(j, "%s", jail_errmsg); + exit(1); + } + for (jp = j->jp; jp < j->jp + j->njp; jp++) + if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) + *++rtjp = *jp; + rval = 0; + if (jailparam_get(rtparams, nrt, + bool_param(j->intparams[IP_ALLOW_DYING]) ? JAIL_DYING : 0) > 0) { + rtjp = rtparams + 1; + for (jp = j->jp, rtjp = rtparams + 1; rtjp < rtparams + nrt; + jp++) { + if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) { + if (!((jp->jp_flags & (JP_BOOL | JP_NOBOOL)) && + jp->jp_valuelen == 0 && + *(int *)jp->jp_value) && + !(rtjp->jp_valuelen == jp->jp_valuelen && + !memcmp(rtjp->jp_value, jp->jp_value, + jp->jp_valuelen))) { + if (dofail) { + jail_warnx(j, "%s cannot be " + "changed after creation", + jp->jp_name); + failed(j); + rval = -1; + } else + rval = 1; + break; + } + rtjp++; + } + } + } + for (rtjp = rtparams + 1; rtjp < rtparams + nrt; rtjp++) + rtjp->jp_name = NULL; + jailparam_free(rtparams, nrt); + return rval; +} + +/* + * Get the jail's jid if it is running. + */ static void -add_ip_addr46(char *value) +running_jid(struct cfjail *j, int dflag) { - char *p, *np; + struct iovec jiov[2]; + const char *pval; + char *ep; + int jid; - for (p = value;; p = np + 1) - { - np = strchr(p, ','); - if (np) - *np = '\0'; - add_ip_addrinfo(AI_NUMERICHOST, p); - if (!np) - break; + if ((pval = string_param(j->intparams[KP_JID]))) { + if (!(jid = strtol(pval, &ep, 10)) || *ep) { + j->jid = -1; + return; + } + *(const void **)&jiov[0].iov_base = "jid"; + jiov[0].iov_len = sizeof("jid"); + jiov[1].iov_base = &jid; + jiov[1].iov_len = sizeof(jid); + } else if ((pval = string_param(j->intparams[KP_NAME]))) { + *(const void **)&jiov[0].iov_base = "name"; + jiov[0].iov_len = sizeof("name"); + jiov[1].iov_len = strlen(pval) + 1; + jiov[1].iov_base = alloca(jiov[1].iov_len); + strcpy(jiov[1].iov_base, pval); + } else { + j->jid = -1; + return; } + j->jid = jail_get(jiov, 2, dflag ? JAIL_DYING : 0); } -#endif static void -add_ip_addrinfo(int ai_flags, char *value) +jail_quoted_warnx(const struct cfjail *j, const char *name_msg, + const char *noname_msg) { - struct addrinfo hints, *ai0, *ai; - int error; -#ifdef INET - char avalue4[INET_ADDRSTRLEN]; - struct in_addr addr4; -#endif -#ifdef INET6 - char avalue6[INET6_ADDRSTRLEN]; - struct in6_addr addr6; -#endif + const char *pval; - /* Look up the hostname (or get the address) */ - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; -#if defined(INET6) && defined(INET) - hints.ai_family = PF_UNSPEC; -#elif defined(INET6) - hints.ai_family = PF_INET6; -#elif defined(INET) - hints.ai_family = PF_INET; -#endif - hints.ai_flags = ai_flags; - error = getaddrinfo(value, NULL, &hints, &ai0); - if (error != 0) - errx(1, "hostname %s: %s", value, gai_strerror(error)); + if ((pval = j->name) || (pval = string_param(j->intparams[KP_JID])) || + (pval = string_param(j->intparams[KP_NAME]))) + warnx("\"%s\" %s", pval, name_msg); + else + warnx("%s", noname_msg); +} - /* Convert the addresses to ASCII so set_param can convert them back. */ - for (ai = ai0; ai; ai = ai->ai_next) - switch (ai->ai_family) { +/* + * Set jail parameters and possible print them out. + */ +static int +jailparam_set_note(const struct cfjail *j, struct jailparam *jp, unsigned njp, + int flags) +{ + char *value; + int jid; + unsigned i; + + jid = jailparam_set(jp, njp, flags); + if (verbose > 0) { + jail_note(j, "jail_set(%s%s)", + (flags & (JAIL_CREATE | JAIL_UPDATE)) == JAIL_CREATE + ? "JAIL_CREATE" : "JAIL_UPDATE", + (flags & JAIL_DYING) ? " | JAIL_DYING" : ""); + for (i = 0; i < njp; i++) { + printf(" %s", jp[i].jp_name); + if (jp[i].jp_value == NULL) + continue; + putchar('='); + value = jailparam_export(jp + i); + if (value == NULL) + err(1, "jailparam_export"); + quoted_print(stdout, value); + free(value); + } + if (jid < 0) + printf(": %s", strerror(errno)); + printf("\n"); + } + return jid; +} + +/* + * Print a jail record. + */ +static void +print_jail(FILE *fp, struct cfjail *j, int oldcl) +{ + struct cfparam *p; + + if (oldcl) { + fprintf(fp, "%d\t", j->jid); + print_param(fp, j->intparams[KP_PATH], ',', 0); + putc('\t', fp); + print_param(fp, j->intparams[KP_HOST_HOSTNAME], ',', 0); + putc('\t', fp); #ifdef INET - case AF_INET: - if (!ip4_ok && (ai_flags & AI_NUMERICHOST) == 0) - break; - memcpy(&addr4, &((struct sockaddr_in *) - (void *)ai->ai_addr)->sin_addr, sizeof(addr4)); - if (inet_ntop(AF_INET, &addr4, avalue4, - INET_ADDRSTRLEN) == NULL) - err(1, "inet_ntop"); - add_ip_addr(&ip4_addr, avalue4); - break; + print_param(fp, j->intparams[KP_IP4_ADDR], ',', 0); +#ifdef INET6 + if (j->intparams[KP_IP4_ADDR] && + !TAILQ_EMPTY(&j->intparams[KP_IP4_ADDR]->val) && + j->intparams[KP_IP6_ADDR] && + !TAILQ_EMPTY(&j->intparams[KP_IP6_ADDR]->val)) + putc(',', fp); #endif +#endif #ifdef INET6 - case AF_INET6: - if (!ip6_ok && (ai_flags & AI_NUMERICHOST) == 0) - break; - memcpy(&addr6, &((struct sockaddr_in6 *) - (void *)ai->ai_addr)->sin6_addr, sizeof(addr6)); - if (inet_ntop(AF_INET6, &addr6, avalue6, - INET6_ADDRSTRLEN) == NULL) - err(1, "inet_ntop"); - add_ip_addr(&ip6_addr, avalue6); - break; + print_param(fp, j->intparams[KP_IP6_ADDR], ',', 0); #endif - } - freeaddrinfo(ai0); + putc('\t', fp); + print_param(fp, j->intparams[IP_COMMAND], ' ', 0); + } else { + fprintf(fp, "jid=%d", j->jid); + TAILQ_FOREACH(p, &j->params, tq) + if (strcmp(p->name, "jid")) { + putc(' ', fp); + print_param(fp, p, ',', 1); + } + } + putc('\n', fp); } +/* + * Print a parameter value, or a name=value pair. + */ static void -quoted_print(FILE *fp, char *str) +print_param(FILE *fp, const struct cfparam *p, int sep, int doname) { - int c, qc; - char *p = str; + const struct cfstring *s, *ts; - /* An empty string needs quoting. */ - if (!*p) { - fputs("\"\"", fp); + if (doname) + fputs(p->name, fp); + if (p == NULL || TAILQ_EMPTY(&p->val)) return; + if (doname) + putc('=', fp); + TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { + quoted_print(fp, s->s); + if (ts != NULL) + putc(sep, fp); } +} - /* - * The value will be surrounded by quotes if it contains spaces - * or quotes. - */ - qc = strchr(p, '\'') ? '"' +/* + * Print a string with quotes around spaces. + */ +static void +quoted_print(FILE *fp, char *str) +{ + int c, qc; + char *p = str; + + qc = !*p ? '"' + : strchr(p, '\'') ? '"' : strchr(p, '"') ? '\'' : strchr(p, ' ') || strchr(p, '\t') ? '"' : 0; if (qc) putc(qc, fp); while ((c = *p++)) { if (c == '\\' || c == qc) putc('\\', fp); putc(c, fp); } if (qc) putc(qc, fp); } static void -set_param(const char *name, char *value) -{ - struct jailparam *param; - char path[PATH_MAX]; - int i; - - static int paramlistsize; - - /* Separate the name from the value, if not done already. */ - if (name == NULL) { - name = value; - if ((value = strchr(value, '='))) - *value++ = '\0'; - } - - /* jail_set won't chdir along with its chroot, so do it here. */ - if (!strcmp(name, "path")) { - /* resolve the path with realpath(3) */ - if (realpath(value, path) != NULL) - value = path; - if (chdir(value) < 0) - err(1, "chdir: %s", value); - } - - /* Check for repeat parameters */ - for (i = 0; i < nparams; i++) - if (!strcmp(name, params[i].jp_name)) { - jailparam_free(params + i, 1); - memcpy(params + i, params + i + 1, - (--nparams - i) * sizeof(struct jailparam)); - break; - } - - /* Make sure there is room for the new param record. */ - if (!nparams) { - paramlistsize = 32; - params = malloc(paramlistsize * sizeof(*params)); - param_values = malloc(paramlistsize * sizeof(*param_values)); - if (params == NULL || param_values == NULL) - err(1, "malloc"); - } else if (nparams >= paramlistsize) { - paramlistsize *= 2; - params = realloc(params, paramlistsize * sizeof(*params)); - param_values = realloc(param_values, - paramlistsize * sizeof(*param_values)); - if (params == NULL) - err(1, "realloc"); - } - - /* Look up the paramter. */ - param_values[nparams] = value; - param = params + nparams++; - if (jailparam_init(param, name) < 0 || - jailparam_import(param, value) < 0) - errx(1, "%s", jail_errmsg); -} - -static void usage(void) { (void)fprintf(stderr, - "usage: jail [-d] [-h] [-i] [-J jid_file] " - "[-l -u username | -U username]\n" - " [-c | -m] param=value ... [command=command ...]\n" - " jail [-r jail]\n"); + "usage: jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n" + " -[cmr] param=value ... [command=command ...]\n" + " jail [-dqv] [-f file] -[cmr] [jail]\n" + " jail [-qv] [-f file] -[rR] ['*' | jail ...]\n" + " jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n" + " [-n jailname] [-s securelevel]\n" + " path hostname [ip[,...]] command ...\n"); exit(1); } Index: stable/9/usr.sbin/jail/jail.conf.5 =================================================================== --- stable/9/usr.sbin/jail/jail.conf.5 (nonexistent) +++ stable/9/usr.sbin/jail/jail.conf.5 (revision 235839) @@ -0,0 +1,231 @@ +.\" Copyright (c) 2012 James Gritton +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd May 23, 2012 +.Dt JAIL.CONF 5 +.Os +.Sh NAME +.Nm jail.conf +.Nd configuration file for +.Xr jail 8 +.Sh DESCRIPTION +A +.Xr jail 8 +configuration file consists of one or more jail definitions statements, +and parameter or variable statements within those jail definitions. +A jail definition statement looks something like a C compound statement. +A parameter statement looks like a C assignment, +including a terminating semicolon. +.Pp +The general syntax of a jail definition is: +.Bd -literal -offset indent +jailname { + parameter = "value"; + parameter = "value"; + ... +} +.Ed +.Pp +Each jail is required to have a +.Va name +at the front of its definition. +This is used by +.Xr jail 8 +to specify a jail on the command line and report the jail status, +and is also passed to the kernel when creating the jail. +.Ss Parameters +A jail is defined by a set of named parameters, specified inside the +jail definition. +See +.Xr jail 8 +for a list of jail parameters passed to the kernel, +as well as internal parameters used when creating and removing jails. +.Pp +A typical parameter has a name and a value. +Some parameters are boolean and may be specified with values of +.Dq true +or +.Dq false , +or as valueless shortcuts, with a +.Dq no +prefix indicating a false value. +For example, these are equivalent: +.Bd -literal -offset indent +allow.mount = "false"; +allow.nomount; +.Ed +.Pp +Other parameters may have more than one value. +A comma-separated list of values may be set in a single statement, +or an existing parameter list may be appended to using +.Dq += : +.Bd -literal -offset indent +ip4.addr = 10.1.1.1, 10.1.1.2, 10.1.1.3; + +ip4.addr = 10.1.1.1; +ip4.addr += 10.1.1.2; +ip4.addr += 10.1.1.3; +.Ed +.Pp +Note the +.Va name +parameter is implicitly set to the name in the jail definition. +.Ss String format +Parameter values, including jail names, can be single tokens or quoted +strings. +A token is any sequence of characters that aren't considered special in +the syntax of the configuration file (such as a semicolon or +whitespace). +If a value contains anything more than letters, numbers, dots, dashes +and underscores, it is advisable to put quote marks around that value. +Either single or double quotes may be used. +.Pp +Special characters may be quoted by preceding them with a backslash. +Common C-style backslash character codes are also supported, including +control characters and octal or hex ASCII codes. +A backslash at the end of a line will ignore the subsequent newline and +continue the string at the start of the next line. +.Ss Variables +A string may use shell-style variable substitution. +A parameter or variable name preceded by a dollar sign, and possibly +enclosed in braces, will be replaced with the value of that parameter or +variable. +For example, a jail's path may be defined in terms of its name or +hostname: +.Bd -literal -offset indent +path = "/var/jail/$name"; + +path = "/var/jail/${host.hostname}"; +.Ed +.Pp +Variable substitution occurs in unquoted tokens or in double-quoted +strings, but not in single-quote strings. +.Pp +A variable is defined in the same way a parameter is, except that the +variable name is preceeded with a dollar sign: +.Bd -literal -offset indent +$parentdir = "/var/jail"; +path = "$parentdir/$name"; +.Ed +.Pp +The difference between parameters and variables is that variables are +only used for substitution, while parameters are used both for +substitution and for passing to the kernel. +.Ss Wildcards +A jail definition with a name of +.Dq * +is used to define wildcard parameters. +Every defined jail will contain both the parameters from its own +definition statement, as well as any parameters in a wildcard +definition. +.Pp +Variable substitution is done on a per-jail basis, even when that +substitution is for a parameter defined in a wildcard section. +This is useful for wildcard parameters based on e.g. a jail's name. +.Pp +Later definitions in the configuration file supersede earlier ones, so a +wildcard section placed before (above) a jail definition defines +parameters that could be changed on a per-jail basis. +Or a wildcard section placed after (below) all jails would contain +parameters that always apply to every jail. +Multiple wildcard statements are allowed, and wildcard parameters may +also be specified outside of a jail definition statement. +.Pp +If hierarchical jails are defined, a partial-matching wildcard +definition may be specified. +For example, a definition with a name of +.Dq foo.* +would apply to jails with names like +.Dq foo.bar +and +.Dq foo.bar.baz . +.Ss Comments +The configuration file may contain comments in the common C, C++, and +shell formats: +.Bd -literal -offset indent +/* This is a C style comment. + * It may span multiple lines. + */ + +// This is a C++ style comment. + +# This is a shell style comment. +.Ed +.Pp +Comments are legal wherever whitespace is allowed, i.e. anywhere except +in the middle of a string or a token. +.Sh EXAMPLES +.Bd -literal +# Typical static defaults: +# Use the rc scripts to start and stop jails. Mount jail's /dev. +exec.start = "/bin/sh /etc/rc"; +exec.stop = "/bin/sh /etc/rc.shutdown"; +exec.clean; +mount.devfs; + +# Dynamic wildcard parameter: +# Base the path off the jail name. +path = "/var/jail/$name"; + +# A typical jail. +foo { + host.hostname = "foo.com"; + ip4.addr = 10.1.1.1, 10.1.1.2, 10.1.1.3; +} + +# This jail overrides the defaults defined above. +bar { + exec.start = ''; + exec.stop = ''; + path = /; + mount.nodevfs; + persist; // Required because there are no processes +} +.Ed +.Sh SEE ALSO +.Xr jail_set 2 +.Xr jail 8 +.Xr jls 8 +.Sh HISTORY +The +.Xr jail 8 +utility appeared in +.Fx 4.0 . +The +.Nm +file was added in +.Fx 9.1 . +.Sh AUTHORS +.An -nosplit +The jail feature was written by +.An Poul-Henning Kamp +for R&D Associates +.Pa http://www.rndassociates.com/ +who contributed it to +.Fx . +.Pp +.An James Gritton +added the extensible jail parameters and configuration file. Property changes on: stable/9/usr.sbin/jail/jail.conf.5 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/9/usr.sbin/jail/jaillex.l =================================================================== --- stable/9/usr.sbin/jail/jaillex.l (nonexistent) +++ stable/9/usr.sbin/jail/jaillex.l (revision 235839) @@ -0,0 +1,233 @@ +%{ +/*- + * Copyright (c) 2011 James Gritton + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include "jailp.h" +#include "y.tab.h" + +#define YY_NO_INPUT +#define YY_NO_UNPUT + +extern int yynerrs; + +static ssize_t text2lval(size_t triml, size_t trimr, int tovar); + +static int instr; +static int lineno = 1; +%} + +%start _ DQ + +%% + + /* Whitespace or equivalent */ +<_>[ \t]+ instr = 0; +<_>#.* ; +<_>\/\/.* ; +<_>\/\*([^*]|(\*+([^*\/])))*\*+\/ { + const char *s; + + for (s = yytext; s < yytext + yyleng; s++) + if (*s == '\n') + lineno++; + instr = 0; + } +<_>\n { + lineno++; + instr = 0; + } + + /* Reserved tokens */ +<_>\+= { + instr = 0; + return PLEQ; + } +<_>[,;={}] { + instr = 0; + return yytext[0]; + } + + /* Atomic (unquoted) strings */ +<_,DQ>[A-Za-z0-9_!%&()\-.:<>?@\[\]^`|~]+ | +<_,DQ>\\(.|\n|[0-7]{1,3}|x[0-9A-Fa-f]{1,2}) | +<_,DQ>[$*+/\\] { + (void)text2lval(0, 0, 0); + return instr ? STR1 : (instr = 1, STR); + } + + /* Single and double quoted strings */ +<_>'([^\'\\]|\\(.|\n))*' { + (void)text2lval(1, 1, 0); + return instr ? STR1 : (instr = 1, STR); + } +<_>\"([^"\\]|\\(.|\n))*\" | +[^\"$\\]([^"\\]|\\(.|\n))*\" { + size_t skip; + ssize_t atvar; + + skip = yytext[0] == '"' ? 1 : 0; + atvar = text2lval(skip, 1, 1); + if (atvar < 0) + BEGIN _; + else { + /* + * The string has a variable inside it. + * Go into DQ mode to get the variable + * and then the rest of the string. + */ + BEGIN DQ; + yyless(atvar); + } + return instr ? STR1 : (instr = 1, STR); + } +\" BEGIN _; + + /* Variables, single-word or bracketed */ +<_,DQ>$[A-Za-z_][A-Za-z_0-9]* { + (void)text2lval(1, 0, 0); + return instr ? VAR1 : (instr = 1, VAR); + } +<_>$\{([^\n{}]|\\(.|\n))*\} | +$\{([^\n\"{}]|\\(.|\n))*\} { + (void)text2lval(2, 1, 0); + return instr ? VAR1 : (instr = 1, VAR); + } + + /* Partially formed bits worth complaining about */ +<_>\/\*([^*]|(\*+([^*\/])))*\** { + warnx("%s line %d: unterminated comment", + cfname, lineno); + yynerrs++; + } +<_>'([^\n'\\]|\\.)* | +<_>\"([^\n\"\\]|\\.)* { + warnx("%s line %d: unterminated string", + cfname, lineno); + yynerrs++; + } +<_>$\{([^\n{}]|\\.)* | +$\{([^\n\"{}]|\\.)* { + warnx("%s line %d: unterminated variable", + cfname, lineno); + yynerrs++; + } + + /* A hack because "<0>" rules aren't allowed */ +<_>. return yytext[0]; +.|\n { + BEGIN _; + yyless(0); + } + +%% + +void +yyerror(const char *s) +{ + if (!yytext) + warnx("%s line %d: %s", cfname, lineno, s); + else if (!yytext[0]) + warnx("%s: unexpected EOF", cfname); + else + warnx("%s line %d: %s: %s", cfname, lineno, yytext, s); +} + +/* + * Copy string from yytext to yylval, handling backslash escapes, + * and optionally stopping at the beginning of a variable. + */ +static ssize_t +text2lval(size_t triml, size_t trimr, int tovar) +{ + char *d; + const char *s, *se; + + yylval.cs = d = emalloc(yyleng - trimr - triml + 1); + se = yytext + (yyleng - trimr); + for (s = yytext + triml; s < se; s++, d++) { + if (*s != '\\') { + if (tovar && *s == '$') { + *d = '\0'; + return s - yytext; + } + if (*s == '\n') + lineno++; + *d = *s; + continue; + } + s++; + if (*s >= '0' && *s <= '7') { + *d = *s - '0'; + if (s + 1 < se && s[1] >= '0' && s[1] <= '7') { + *d = 010 * *d + (*++s - '0'); + if (s + 1 < se && s[1] >= '0' && s[1] <= '7') + *d = 010 * *d + (*++s - '0'); + } + continue; + } + switch (*s) { + case 'a': *d = '\a'; break; + case 'b': *d = '\b'; break; + case 'f': *d = '\f'; break; + case 'n': *d = '\n'; break; + case 'r': *d = '\r'; break; + case 't': *d = '\t'; break; + case 'v': *d = '\v'; break; + case '\n': d--; lineno++; break; + default: *d = *s; break; + case 'x': + *d = 0; + if (s + 1 >= se) + break; + if (s[1] >= '0' && s[1] <= '9') + *d = *++s - '0'; + else if (s[1] >= 'A' && s[1] <= 'F') + *d = *++s + (0xA - 'A'); + else if (s[1] >= 'a' && s[1] <= 'a') + *d = *++s + (0xa - 'a'); + else + break; + if (s + 1 >= se) + break; + if (s[1] >= '0' && s[1] <= '9') + *d = *d * 0x10 + (*++s - '0'); + else if (s[1] >= 'A' && s[1] <= 'F') + *d = *d * 0x10 + (*++s + (0xA - 'A')); + else if (s[1] >= 'a' && s[1] <= 'a') + *d = *d * 0x10 + (*++s + (0xa - 'a')); + } + } + *d = '\0'; + return -1; +} Property changes on: stable/9/usr.sbin/jail/jaillex.l ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/9/usr.sbin/jail/jailp.h =================================================================== --- stable/9/usr.sbin/jail/jailp.h (nonexistent) +++ stable/9/usr.sbin/jail/jailp.h (revision 235839) @@ -0,0 +1,233 @@ +/*- + * Copyright (c) 2011 James Gritton. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include + +#define CONF_FILE "/etc/jail.conf" + +#define DEP_FROM 0 +#define DEP_TO 1 + +#define DF_SEEN 0x01 /* Dependency has been followed */ +#define DF_LIGHT 0x02 /* Implied dependency on jail existence only */ +#define DF_NOFAIL 0x04 /* Don't propigate failed jails */ + +#define PF_VAR 0x01 /* This is a variable, not a true parameter */ +#define PF_APPEND 0x02 /* Append to existing parameter list */ +#define PF_BAD 0x04 /* Unable to resolve parameter value */ +#define PF_INTERNAL 0x08 /* Internal parameter, not passed to kernel */ +#define PF_BOOL 0x10 /* Boolean parameter */ +#define PF_INT 0x20 /* Integer parameter */ +#define PF_CONV 0x40 /* Parameter duplicated in converted form */ + +#define JF_START 0x0001 /* -c */ +#define JF_SET 0x0002 /* -m */ +#define JF_STOP 0x0004 /* -r */ +#define JF_DEPEND 0x0008 /* Operation required by dependency */ +#define JF_WILD 0x0010 /* Not specified on the command line */ +#define JF_FAILED 0x0020 /* Operation failed */ +#define JF_PARAMS 0x0040 /* Parameters checked and imported */ +#define JF_RDTUN 0x0080 /* Create-only parameter check has been done */ +#define JF_PERSIST 0x0100 /* Jail is temporarily persistent */ +#define JF_TIMEOUT 0x0200 /* A command (or process kill) timed out */ +#define JF_SLEEPQ 0x0400 /* Waiting on a command and/or timeout */ + +#define JF_OP_MASK (JF_START | JF_SET | JF_STOP) +#define JF_RESTART (JF_START | JF_STOP) +#define JF_START_SET (JF_START | JF_SET) +#define JF_SET_RESTART (JF_SET | JF_STOP) +#define JF_START_SET_RESTART (JF_START | JF_SET | JF_STOP) +#define JF_DO_STOP(js) (((js) & (JF_SET | JF_STOP)) == JF_STOP) + +enum intparam { + IP__NULL = 0, /* Null command */ + IP_ALLOW_DYING, /* Allow making changes to a dying jail */ + IP_COMMAND, /* Command run inside jail at creation */ + IP_DEPEND, /* Jail starts after (stops before) another */ + IP_EXEC_CLEAN, /* Run commands in a clean environment */ + IP_EXEC_CONSOLELOG, /* Redirect optput for commands run in jail */ + IP_EXEC_FIB, /* Run jailed commands with this FIB */ + IP_EXEC_JAIL_USER, /* Run jailed commands as this user */ + IP_EXEC_POSTSTART, /* Commands run outside jail after creating */ + IP_EXEC_POSTSTOP, /* Commands run outside jail after removing */ + IP_EXEC_PRESTART, /* Commands run outside jail before creating */ + IP_EXEC_PRESTOP, /* Commands run outside jail before removing */ + IP_EXEC_START, /* Commands run inside jail on creation */ + IP_EXEC_STOP, /* Commands run inside jail on removal */ + IP_EXEC_SYSTEM_JAIL_USER,/* Get jail_user from system passwd file */ + IP_EXEC_SYSTEM_USER, /* Run non-jailed commands as this user */ + IP_EXEC_TIMEOUT, /* Time to wait for a command to complete */ +#if defined(INET) || defined(INET6) + IP_INTERFACE, /* Add IP addresses to this interface */ + IP_IP_HOSTNAME, /* Get jail IP address(es) from hostname */ +#endif + IP_MOUNT, /* Mount points in fstab(5) form */ + IP_MOUNT_DEVFS, /* Mount /dev under prison root */ + IP_MOUNT_FSTAB, /* A standard fstab(5) file */ + IP_STOP_TIMEOUT, /* Time to wait after sending SIGTERM */ + IP_VNET_INTERFACE, /* Assign interface(s) to vnet jail */ +#ifdef INET + IP__IP4_IFADDR, /* Copy of ip4.addr with interface/netmask */ +#endif +#ifdef INET6 + IP__IP6_IFADDR, /* Copy of ip6.addr with interface/prefixlen */ +#endif + IP__MOUNT_FROM_FSTAB, /* Line from mount.fstab file */ + IP__OP, /* Placeholder for requested operation */ + KP_ALLOW_CHFLAGS, + KP_ALLOW_MOUNT, + KP_ALLOW_RAW_SOCKETS, + KP_ALLOW_SET_HOSTNAME, + KP_ALLOW_SOCKET_AF, + KP_ALLOW_SYSVIPC, + KP_DEVFS_RULESET, + KP_ENFORCE_STATFS, + KP_HOST_HOSTNAME, +#ifdef INET + KP_IP4_ADDR, +#endif +#ifdef INET6 + KP_IP6_ADDR, +#endif + KP_JID, + KP_NAME, + KP_PATH, + KP_PERSIST, + KP_SECURELEVEL, + KP_VNET, + IP_NPARAM +}; + +STAILQ_HEAD(cfvars, cfvar); + +struct cfvar { + STAILQ_ENTRY(cfvar) tq; + char *name; + size_t pos; +}; + +TAILQ_HEAD(cfstrings, cfstring); + +struct cfstring { + TAILQ_ENTRY(cfstring) tq; + char *s; + size_t len; + struct cfvars vars; +}; + +TAILQ_HEAD(cfparams, cfparam); + +struct cfparam { + TAILQ_ENTRY(cfparam) tq; + char *name; + struct cfstrings val; + unsigned flags; + int gen; +}; + +TAILQ_HEAD(cfjails, cfjail); +STAILQ_HEAD(cfdepends, cfdepend); + +struct cfjail { + TAILQ_ENTRY(cfjail) tq; + char *name; + char *comline; + struct cfparams params; + struct cfdepends dep[2]; + struct cfjails *queue; + struct cfparam *intparams[IP_NPARAM]; + struct cfstring *comstring; + struct jailparam *jp; + struct timespec timeout; + const enum intparam *comparam; + unsigned flags; + int jid; + int seq; + int pstatus; + int ndeps; + int njp; + int nprocs; +}; + +struct cfdepend { + STAILQ_ENTRY(cfdepend) tq[2]; + struct cfjail *j[2]; + unsigned flags; +}; + +extern void *emalloc(size_t); +extern void *erealloc(void *, size_t); +extern char *estrdup(const char *); +extern int create_jail(struct cfjail *j); +extern void failed(struct cfjail *j); +extern void jail_note(const struct cfjail *j, const char *fmt, ...); +extern void jail_warnx(const struct cfjail *j, const char *fmt, ...); + +extern int next_command(struct cfjail *j); +extern int finish_command(struct cfjail *j); +extern struct cfjail *next_proc(int nonblock); + +extern void load_config(void); +extern struct cfjail *add_jail(void); +extern void add_param(struct cfjail *j, const struct cfparam *p, + enum intparam ipnum, const char *value); +extern int bool_param(const struct cfparam *p); +extern int int_param(const struct cfparam *p, int *ip); +extern const char *string_param(const struct cfparam *p); +extern int check_intparams(struct cfjail *j); +extern int import_params(struct cfjail *j); +extern int equalopts(const char *opt1, const char *opt2); +extern int wild_jail_name(const char *wname); +extern int wild_jail_match(const char *jname, const char *wname); + +extern void dep_setup(int docf); +extern int dep_check(struct cfjail *j); +extern void dep_done(struct cfjail *j, unsigned flags); +extern void dep_reset(struct cfjail *j); +extern struct cfjail *next_jail(void); +extern int start_state(const char *target, int docf, unsigned state, + int running); +extern void requeue(struct cfjail *j, struct cfjails *queue); + +extern void yyerror(const char *); +extern int yylex(void); +extern int yyparse(void); + +extern struct cfjails cfjails; +extern struct cfjails ready; +extern struct cfjails depend; +extern const char *cfname; +extern int note_remove; +extern int paralimit; +extern int verbose; Property changes on: stable/9/usr.sbin/jail/jailp.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/9/usr.sbin/jail/jailparse.y =================================================================== --- stable/9/usr.sbin/jail/jailparse.y (nonexistent) +++ stable/9/usr.sbin/jail/jailparse.y (revision 235839) @@ -0,0 +1,216 @@ +%{ +/*- + * Copyright (c) 2011 James Gritton + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "jailp.h" + +#ifdef DEBUG +#define YYDEBUG 1 +#endif +%} + +%union { + struct cfjail *j; + struct cfparams *pp; + struct cfparam *p; + struct cfstrings *ss; + struct cfstring *s; + char *cs; +} + +%token PLEQ +%token STR STR1 VAR VAR1 + +%type jail +%type param_l +%type

param name +%type value +%type string + +%% + +/* + * A config file is a series of jails (containing parameters) and jail-less + * parameters which realy belong to a global pseudo-jail. + */ +conf : + ; + | conf jail + ; + | conf param ';' + { + struct cfjail *j; + + j = TAILQ_LAST(&cfjails, cfjails); + if (!j || strcmp(j->name, "*")) { + j = add_jail(); + j->name = estrdup("*"); + } + TAILQ_INSERT_TAIL(&j->params, $2, tq); + } + | conf ';' + +jail : STR '{' param_l '}' + { + $$ = add_jail(); + $$->name = $1; + TAILQ_CONCAT(&$$->params, $3, tq); + free($3); + } + ; + +param_l : + { + $$ = emalloc(sizeof(struct cfparams)); + TAILQ_INIT($$); + } + | param_l param ';' + { + $$ = $1; + TAILQ_INSERT_TAIL($$, $2, tq); + } + | param_l ';' + ; + +/* + * Parameters have a name and an optional list of value strings, + * which may have "+=" or "=" preceeding them. + */ +param : name + { + $$ = $1; + } + | name '=' value + { + $$ = $1; + TAILQ_CONCAT(&$$->val, $3, tq); + free($3); + } + | name PLEQ value + { + $$ = $1; + TAILQ_CONCAT(&$$->val, $3, tq); + $$->flags |= PF_APPEND; + free($3); + } + | name value + { + $$ = $1; + TAILQ_CONCAT(&$$->val, $2, tq); + free($2); + } + | error + { + } + ; + +/* + * A parameter has a fixed name. A variable definition looks just like a + * parameter except that the name is a variable. + */ +name : STR + { + $$ = emalloc(sizeof(struct cfparam)); + $$->name = $1; + TAILQ_INIT(&$$->val); + $$->flags = 0; + } + | VAR + { + $$ = emalloc(sizeof(struct cfparam)); + $$->name = $1; + TAILQ_INIT(&$$->val); + $$->flags = PF_VAR; + } + ; + +value : string + { + $$ = emalloc(sizeof(struct cfstrings)); + TAILQ_INIT($$); + TAILQ_INSERT_TAIL($$, $1, tq); + } + | value ',' string + { + $$ = $1; + TAILQ_INSERT_TAIL($$, $3, tq); + } + ; + +/* + * Strings may be passed in pieces, because of quoting and/or variable + * interpolation. Reassemble them into a single string. + */ +string : STR + { + $$ = emalloc(sizeof(struct cfstring)); + $$->s = $1; + $$->len = strlen($1); + STAILQ_INIT(&$$->vars); + } + | VAR + { + struct cfvar *v; + + $$ = emalloc(sizeof(struct cfstring)); + $$->s = estrdup(""); + $$->len = 0; + STAILQ_INIT(&$$->vars); + v = emalloc(sizeof(struct cfvar)); + v->name = $1; + v->pos = 0; + STAILQ_INSERT_TAIL(&$$->vars, v, tq); + } + | string STR1 + { + size_t len1; + + $$ = $1; + len1 = strlen($2); + $$->s = erealloc($$->s, $$->len + len1 + 1); + strcpy($$->s + $$->len, $2); + free($2); + $$->len += len1; + } + | string VAR1 + { + struct cfvar *v; + + $$ = $1; + v = emalloc(sizeof(struct cfvar)); + v->name = $2; + v->pos = $$->len; + STAILQ_INSERT_TAIL(&$$->vars, v, tq); + } + ; + +%% Property changes on: stable/9/usr.sbin/jail/jailparse.y ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/9/usr.sbin/jail/state.c =================================================================== --- stable/9/usr.sbin/jail/state.c (nonexistent) +++ stable/9/usr.sbin/jail/state.c (revision 235839) @@ -0,0 +1,469 @@ +/*- + * Copyright (c) 2011 James Gritton + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include + +#include "jailp.h" + +struct cfjails ready = TAILQ_HEAD_INITIALIZER(ready); +struct cfjails depend = TAILQ_HEAD_INITIALIZER(depend); + +static void dep_add(struct cfjail *from, struct cfjail *to, unsigned flags); +static int cmp_jailptr(const void *a, const void *b); +static int cmp_jailptr_name(const void *a, const void *b); +static struct cfjail *find_jail(const char *name); +static int running_jid(const char *name, int flags); + +static struct cfjail **jails_byname; +static size_t njails; + +/* + * Set up jail dependency lists. + */ +void +dep_setup(int docf) +{ + struct cfjail *j, *dj; + struct cfparam *p; + struct cfstring *s; + struct cfdepend *d; + const char *cs; + char *pname; + size_t plen; + int error, deps, ldeps; + + if (!docf) { + /* + * With no config file, let "depend" for a single jail + * look at currently running jails. + */ + if ((j = TAILQ_FIRST(&cfjails)) && + (p = j->intparams[IP_DEPEND])) { + TAILQ_FOREACH(s, &p->val, tq) { + if (running_jid(s->s, 0) < 0) { + warnx("depends on nonexistent jail " + "\"%s\"", s->s); + j->flags |= JF_FAILED; + } + } + } + return; + } + + njails = 0; + TAILQ_FOREACH(j, &cfjails, tq) + njails++; + jails_byname = emalloc(njails * sizeof(struct cfjail *)); + njails = 0; + TAILQ_FOREACH(j, &cfjails, tq) + jails_byname[njails++] = j; + qsort(jails_byname, njails, sizeof(struct cfjail *), cmp_jailptr); + error = 0; + deps = 0; + ldeps = 0; + plen = 0; + pname = NULL; + TAILQ_FOREACH(j, &cfjails, tq) { + if (j->flags & JF_FAILED) + continue; + if ((p = j->intparams[IP_DEPEND])) { + TAILQ_FOREACH(s, &p->val, tq) { + dj = find_jail(s->s); + if (dj != NULL) { + deps++; + dep_add(j, dj, 0); + } else { + jail_warnx(j, + "depends on undefined jail \"%s\"", + s->s); + j->flags |= JF_FAILED; + } + } + } + /* A jail has an implied dependency on its parent. */ + if ((cs = strrchr(j->name, '.'))) + { + if (plen < (size_t)(cs - j->name + 1)) { + plen = (cs - j->name) + 1; + pname = erealloc(pname, plen); + } + strlcpy(pname, j->name, plen); + dj = find_jail(pname); + if (dj != NULL) { + ldeps++; + dep_add(j, dj, DF_LIGHT); + } + } + } + + /* Look for dependency loops. */ + if (deps && (deps > 1 || ldeps)) { + (void)start_state(NULL, 0, 0, 0); + while ((j = TAILQ_FIRST(&ready))) { + requeue(j, &cfjails); + dep_done(j, DF_NOFAIL); + } + while ((j = TAILQ_FIRST(&depend)) != NULL) { + jail_warnx(j, "dependency loop"); + j->flags |= JF_FAILED; + do { + requeue(j, &cfjails); + dep_done(j, DF_NOFAIL); + } while ((j = TAILQ_FIRST(&ready))); + } + TAILQ_FOREACH(j, &cfjails, tq) + STAILQ_FOREACH(d, &j->dep[DEP_FROM], tq[DEP_FROM]) + d->flags &= ~DF_SEEN; + } + if (pname != NULL) + free(pname); +} + +/* + * Return if a jail has dependencies. + */ +int +dep_check(struct cfjail *j) +{ + int reset, depfrom, depto, ndeps, rev; + struct cfjail *dj; + struct cfdepend *d; + + static int bits[] = { 0, 1, 1, 2, 1, 2, 2, 3 }; + + if (j->ndeps == 0) + return 0; + ndeps = 0; + if ((rev = JF_DO_STOP(j->flags))) { + depfrom = DEP_TO; + depto = DEP_FROM; + } else { + depfrom = DEP_FROM; + depto = DEP_TO; + } + STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) { + if (d->flags & DF_SEEN) + continue; + dj = d->j[depto]; + if (dj->flags & JF_FAILED) { + if (!(j->flags & (JF_DEPEND | JF_FAILED)) && + verbose >= 0) + jail_warnx(j, "skipped"); + j->flags |= JF_FAILED; + continue; + } + /* + * The dependee's state may be set (or changed) as a result of + * being in a dependency it wasn't in earlier. + */ + reset = 0; + if (bits[dj->flags & JF_OP_MASK] <= 1) { + if (!(dj->flags & JF_OP_MASK)) { + reset = 1; + dj->flags |= JF_DEPEND; + requeue(dj, &ready); + } + /* Set or change the dependee's state. */ + switch (j->flags & JF_OP_MASK) { + case JF_START: + dj->flags |= JF_START; + break; + case JF_SET: + if (!(dj->flags & JF_OP_MASK)) + dj->flags |= JF_SET; + else if (dj->flags & JF_STOP) + dj->flags |= JF_START; + break; + case JF_STOP: + case JF_RESTART: + if (!(dj->flags & JF_STOP)) + reset = 1; + dj->flags |= JF_STOP; + if (dj->flags & JF_SET) + dj->flags ^= (JF_START | JF_SET); + break; + } + } + if (reset) + dep_reset(dj); + if (!((d->flags & DF_LIGHT) && + (rev ? dj->jid < 0 : dj->jid > 0))) + ndeps++; + } + if (ndeps == 0) + return 0; + requeue(j, &depend); + return 1; +} + +/* + * Resolve any dependencies from a finished jail. + */ +void +dep_done(struct cfjail *j, unsigned flags) +{ + struct cfjail *dj; + struct cfdepend *d; + int depfrom, depto; + + if (JF_DO_STOP(j->flags)) { + depfrom = DEP_TO; + depto = DEP_FROM; + } else { + depfrom = DEP_FROM; + depto = DEP_TO; + } + STAILQ_FOREACH(d, &j->dep[depto], tq[depto]) { + if ((d->flags & DF_SEEN) | (flags & ~d->flags & DF_LIGHT)) + continue; + d->flags |= DF_SEEN; + dj = d->j[depfrom]; + if (!(flags & DF_NOFAIL) && (j->flags & JF_FAILED) && + (j->flags & (JF_OP_MASK | JF_DEPEND)) != + (JF_SET | JF_DEPEND)) { + if (!(dj->flags & (JF_DEPEND | JF_FAILED)) && + verbose >= 0) + jail_warnx(dj, "skipped"); + dj->flags |= JF_FAILED; + } + if (!--dj->ndeps && dj->queue == &depend) + requeue(dj, &ready); + } +} + +/* + * Count a jail's dependencies and mark them as unseen. + */ +void +dep_reset(struct cfjail *j) +{ + int depfrom; + struct cfdepend *d; + + depfrom = JF_DO_STOP(j->flags) ? DEP_TO : DEP_FROM; + j->ndeps = 0; + STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) + j->ndeps++; +} + +/* + * Find the next jail ready to do something. + */ +struct cfjail * +next_jail(void) +{ + struct cfjail *j; + + if (!(j = next_proc(!TAILQ_EMPTY(&ready))) && + (j = TAILQ_FIRST(&ready)) && JF_DO_STOP(j->flags) && + (j = TAILQ_LAST(&ready, cfjails)) && !JF_DO_STOP(j->flags)) { + TAILQ_FOREACH_REVERSE(j, &ready, cfjails, tq) + if (JF_DO_STOP(j->flags)) + break; + } + if (j != NULL) + requeue(j, &cfjails); + return j; +} + +/* + * Set jails to the proper start state. + */ +int +start_state(const char *target, int docf, unsigned state, int running) +{ + struct iovec jiov[6]; + struct cfjail *j, *tj; + int jid; + char namebuf[MAXHOSTNAMELEN]; + + if (!target || (!docf && state != JF_STOP) || + (!running && !strcmp(target, "*"))) { + /* + * For a global wildcard (including no target specified), + * set the state on all jails and start with those that + * have no dependencies. + */ + TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { + j->flags = (j->flags & JF_FAILED) | state | + (docf ? JF_WILD : 0); + dep_reset(j); + requeue(j, j->ndeps ? &depend : &ready); + } + } else if (wild_jail_name(target)) { + /* + * For targets specified singly, or with a non-global wildcard, + * set their state and call them ready (even if there are + * dependencies). Leave everything else unqueued for now. + */ + if (running) { + /* + * -R matches its wildcards against currently running + * jails, not against the config file. + */ + *(const void **)&jiov[0].iov_base = "lastjid"; + jiov[0].iov_len = sizeof("lastjid"); + jiov[1].iov_base = &jid; + jiov[1].iov_len = sizeof(jid); + *(const void **)&jiov[2].iov_base = "jid"; + jiov[2].iov_len = sizeof("jid"); + jiov[3].iov_base = &jid; + jiov[3].iov_len = sizeof(jid); + *(const void **)&jiov[4].iov_base = "name"; + jiov[4].iov_len = sizeof("name"); + jiov[5].iov_base = &namebuf; + jiov[5].iov_len = sizeof(namebuf); + for (jid = 0; jail_get(jiov, 6, 0) > 0; ) { + if (wild_jail_match(namebuf, target)) { + j = add_jail(); + j->name = estrdup(namebuf); + j->jid = jid; + j->flags = (j->flags & JF_FAILED) | + state | JF_WILD; + dep_reset(j); + requeue(j, &ready); + } + } + } else { + TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { + if (wild_jail_match(j->name, target)) { + j->flags = (j->flags & JF_FAILED) | + state | JF_WILD; + dep_reset(j); + requeue(j, &ready); + } + } + } + } else { + j = find_jail(target); + if (j == NULL && state == JF_STOP) { + /* Allow -[rR] to specify a currently running jail. */ + if ((jid = running_jid(target, JAIL_DYING)) > 0) { + j = add_jail(); + j->name = estrdup(target); + j->jid = jid; + } + } + if (j == NULL) { + warnx("\"%s\" not found", target); + return -1; + } + j->flags = (j->flags & JF_FAILED) | state; + dep_reset(j); + requeue(j, &ready); + } + return 0; +} + +/* + * Move a jail to a new list. + */ +void +requeue(struct cfjail *j, struct cfjails *queue) +{ + if (j->queue != queue) { + TAILQ_REMOVE(j->queue, j, tq); + TAILQ_INSERT_TAIL(queue, j, tq); + j->queue = queue; + } +} + +/* + * Add a dependency edge between two jails. + */ +static void +dep_add(struct cfjail *from, struct cfjail *to, unsigned flags) +{ + struct cfdepend *d; + + d = emalloc(sizeof(struct cfdepend)); + d->flags = flags; + d->j[DEP_FROM] = from; + d->j[DEP_TO] = to; + STAILQ_INSERT_TAIL(&from->dep[DEP_FROM], d, tq[DEP_FROM]); + STAILQ_INSERT_TAIL(&to->dep[DEP_TO], d, tq[DEP_TO]); +} + +/* + * Compare jail pointers for qsort/bsearch. + */ +static int +cmp_jailptr(const void *a, const void *b) +{ + return strcmp((*((struct cfjail * const *)a))->name, + ((*(struct cfjail * const *)b))->name); +} + +static int +cmp_jailptr_name(const void *a, const void *b) +{ + return strcmp((const char *)a, ((*(struct cfjail * const *)b))->name); +} + +/* + * Find a jail object by name. + */ +static struct cfjail * +find_jail(const char *name) +{ + struct cfjail **jp; + + jp = bsearch(name, jails_byname, njails, sizeof(struct cfjail *), + cmp_jailptr_name); + return jp ? *jp : NULL; +} + +/* + * Return the named jail's jid if it is running, and -1 if it isn't. + */ +static int +running_jid(const char *name, int flags) +{ + struct iovec jiov[2]; + char *ep; + int jid; + + if ((jid = strtol(name, &ep, 10)) && !*ep) { + *(const void **)&jiov[0].iov_base = "jid"; + jiov[0].iov_len = sizeof("jid"); + jiov[1].iov_base = &jid; + jiov[1].iov_len = sizeof(jid); + } else { + *(const void **)&jiov[0].iov_base = "name"; + jiov[0].iov_len = sizeof("name"); + jiov[1].iov_len = strlen(name) + 1; + jiov[1].iov_base = alloca(jiov[1].iov_len); + strcpy(jiov[1].iov_base, name); + } + return jail_get(jiov, 2, flags); +} Property changes on: stable/9/usr.sbin/jail/state.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/9/usr.sbin/jail =================================================================== --- stable/9/usr.sbin/jail (revision 235838) +++ stable/9/usr.sbin/jail (revision 235839) Property changes on: stable/9/usr.sbin/jail ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,4 ## Merged /head/usr.sbin/jail:r234712,234744,234934,234988,235294,235335 Merged /projects/multi-fibv6/head/usr.sbin/jail:r230929-231848 Merged /projects/head_mfi/usr.sbin/jail:r233621 Merged /projects/jailconf/usr.sbin/jail:r214085-232242