A new jail(8) with a configuration file, ultimately to replace the work
currently done by /etc/rc.d/jail. MFC after: 3 months
This commit is contained in:
commit
91b24c185b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=234712
@ -247,44 +247,6 @@ They return \-1 on failure, and set
|
||||
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/<pid>/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
|
||||
@ -415,7 +377,7 @@ 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.
|
||||
|
@ -3,9 +3,14 @@
|
||||
.include <bsd.own.mk>
|
||||
|
||||
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
|
||||
@ -14,4 +19,6 @@ CFLAGS+= -DINET6
|
||||
CFLAGS+= -DINET
|
||||
.endif
|
||||
|
||||
CLEANFILES= y.output
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
857
usr.sbin/jail/command.c
Normal file
857
usr.sbin/jail/command.c
Normal file
@ -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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <kvm.h>
|
||||
#include <login_cap.h>
|
||||
#include <paths.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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 0:
|
||||
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;
|
||||
}
|
831
usr.sbin/jail/config.c
Normal file
831
usr.sbin/jail/config.c
Normal file
@ -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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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 != 0) {
|
||||
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 != 0)
|
||||
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 != 0)
|
||||
j->intparams[ipnum] = np;
|
||||
else
|
||||
for (ipnum = 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);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
.\"
|
||||
.\" 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
|
||||
@ -24,116 +23,68 @@
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\"
|
||||
.\" ----------------------------------------------------------------------------
|
||||
.\" "THE BEER-WARE LICENSE" (Revision 42):
|
||||
.\" <phk@FreeBSD.org> 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 April 26, 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 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 J Ar jid_file
|
||||
.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 d
|
||||
Allow making changes to a dying jail.
|
||||
.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
|
||||
parameter.
|
||||
.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.
|
||||
.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.
|
||||
.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
|
||||
.Va securelevel
|
||||
parameter.
|
||||
.It Fl u Ar username
|
||||
The user name from host environment as whom the
|
||||
.Ar command
|
||||
should run.
|
||||
.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
|
||||
The jail
|
||||
.Va jid
|
||||
and
|
||||
.Va name
|
||||
parameters (if specified) must not refer to an existing jail.
|
||||
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
|
||||
@ -141,27 +92,158 @@ One of the
|
||||
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.
|
||||
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
|
||||
At least one of the
|
||||
.Fl c ,
|
||||
.Fl m
|
||||
or
|
||||
Other available options are:
|
||||
.Bl -tag -width indent
|
||||
.It Fl d
|
||||
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 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 parameters used to start the jail.
|
||||
.It Fl l
|
||||
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
|
||||
options must be specified.
|
||||
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 the
|
||||
.Va securelevel
|
||||
parameter.
|
||||
.It Fl u Ar username
|
||||
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 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
|
||||
.Ar Parameters
|
||||
are listed in
|
||||
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
|
||||
.Fl r
|
||||
and
|
||||
.Fl R
|
||||
options can also remove running jails that aren't in the
|
||||
.Xr jail.conf 5
|
||||
file, specified by name or jid.
|
||||
.P
|
||||
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
|
||||
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
|
||||
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 .
|
||||
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, following the options.
|
||||
form.
|
||||
Some parameters are boolean, and do not have a value but are set by the
|
||||
name alone with or without a
|
||||
.Dq no
|
||||
@ -169,41 +251,35 @@ 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:
|
||||
.Ar path ,
|
||||
.Ar hostname ,
|
||||
.Ar ip ,
|
||||
They can also be given the values
|
||||
.Dq true
|
||||
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
|
||||
.Fl c
|
||||
and
|
||||
.Fl m
|
||||
options don't apply (and must not exist).
|
||||
.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
|
||||
@ -231,14 +307,21 @@ 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.
|
||||
@ -252,7 +335,7 @@ assigned to itself.
|
||||
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
|
||||
@ -277,8 +360,20 @@ A set of IPv6 options for the prison, the counterparts to
|
||||
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
|
||||
@ -314,13 +409,24 @@ 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
|
||||
@ -345,10 +451,13 @@ 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
|
||||
@ -459,6 +568,171 @@ 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
|
||||
@ -476,7 +750,6 @@ 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
|
||||
@ -489,20 +762,8 @@ 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.
|
||||
@ -520,8 +781,9 @@ Do what was described in
|
||||
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"
|
||||
@ -619,7 +881,7 @@ 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
|
||||
@ -633,15 +895,6 @@ 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
|
||||
@ -652,11 +905,6 @@ 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
|
||||
@ -674,36 +922,47 @@ 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.
|
||||
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.
|
||||
Create an entry for the jail in
|
||||
.Pa /etc/jail.conf :
|
||||
.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
|
||||
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
|
||||
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.
|
||||
To start a virtual server environment,
|
||||
.Pa /etc/rc
|
||||
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
|
||||
jail -c testjail
|
||||
.Ed
|
||||
.Pp
|
||||
A few warnings may be produced; however, it should all work properly.
|
||||
You should be able to see
|
||||
.Xr inetd 8 ,
|
||||
.Xr syslogd 8 ,
|
||||
@ -726,15 +985,6 @@ Please refer to the
|
||||
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 ,
|
||||
@ -742,9 +992,8 @@ Normal machine shutdown commands, such as
|
||||
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
|
||||
@ -754,21 +1003,27 @@ 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
|
||||
@ -888,7 +1143,7 @@ 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.
|
||||
@ -900,12 +1155,12 @@ environment of the first jail.
|
||||
.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 ,
|
||||
@ -927,6 +1182,8 @@ utility appeared in
|
||||
.Fx 4.0 .
|
||||
Hierarchical/extensible jails were introduced in
|
||||
.Fx 8.0 .
|
||||
The configuration file was introduced in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The jail feature was written by
|
||||
@ -947,14 +1204,10 @@ originally done by
|
||||
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
|
||||
|
1258
usr.sbin/jail/jail.c
1258
usr.sbin/jail/jail.c
File diff suppressed because it is too large
Load Diff
231
usr.sbin/jail/jail.conf.5
Normal file
231
usr.sbin/jail/jail.conf.5
Normal file
@ -0,0 +1,231 @@
|
||||
.\" 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$
|
||||
.\"
|
||||
.Dd October 20, 2010
|
||||
.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 assigment,
|
||||
including a terminating semicolon.
|
||||
.Pp
|
||||
The general syntax of a jail definition is:
|
||||
.Pp
|
||||
.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 undescores, 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 preceeding 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 preceeded 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 substition 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
|
||||
}
|
||||
.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.0 .
|
||||
.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.
|
232
usr.sbin/jail/jaillex.l
Normal file
232
usr.sbin/jail/jaillex.l
Normal file
@ -0,0 +1,232 @@
|
||||
%{
|
||||
/*-
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <err.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "jailp.h"
|
||||
#include "y.tab.h"
|
||||
|
||||
#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))*\" |
|
||||
<DQ>[^\"$\\]([^"\\]|\\(.|\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);
|
||||
}
|
||||
<DQ>\" 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))*\} |
|
||||
<DQ>$\{([^\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{}]|\\.)* |
|
||||
<DQ>$\{([^\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;
|
||||
}
|
232
usr.sbin/jail/jailp.h
Normal file
232
usr.sbin/jail/jailp.h
Normal file
@ -0,0 +1,232 @@
|
||||
/*-
|
||||
* 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 <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/jail.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <jail.h>
|
||||
|
||||
#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_ALLOW_DYING = 1, /* 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;
|
216
usr.sbin/jail/jailparse.y
Normal file
216
usr.sbin/jail/jailparse.y
Normal file
@ -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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 <cs> STR STR1 VAR VAR1
|
||||
|
||||
%type <j> jail
|
||||
%type <pp> param_l
|
||||
%type <p> param name
|
||||
%type <ss> value
|
||||
%type <s> 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);
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
469
usr.sbin/jail/state.c
Normal file
469
usr.sbin/jail/state.c
Normal file
@ -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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user