Allow the kern.osrelease and kern.osreldate sysctl values to be set in a
jail's creation parameters. This allows the kernel version to be reliably spoofed within the jail whether examined directly with sysctl or indirectly with the uname -r and -K options. The values can only be set at jail creation time, to eliminate the need for any locking when accessing the values via sysctl. The overridden values are inherited by nested jails (unless the config for the nested jails also overrides the values). There is no sanity or range checking, other than disallowing an empty release string or a zero release date, by design. The system administrator is trusted to set sane values. Setting values that are newer than the actual running kernel will likely cause compatibility problems. Differential Revision: https://reviews.freebsd.org/D1948 Relnotes: yes
This commit is contained in:
parent
2d1731b857
commit
b96bd95b85
@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/imgact.h>
|
||||
#include <sys/imgact_elf.h>
|
||||
#include <sys/jail.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
@ -1007,7 +1008,8 @@ __elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp)
|
||||
AUXARGS_ENTRY(pos, AT_BASE, args->base);
|
||||
if (imgp->execpathp != 0)
|
||||
AUXARGS_ENTRY(pos, AT_EXECPATH, imgp->execpathp);
|
||||
AUXARGS_ENTRY(pos, AT_OSRELDATE, osreldate);
|
||||
AUXARGS_ENTRY(pos, AT_OSRELDATE,
|
||||
imgp->proc->p_ucred->cr_prison->pr_osreldate);
|
||||
if (imgp->canary != 0) {
|
||||
AUXARGS_ENTRY(pos, AT_CANARY, imgp->canary);
|
||||
AUXARGS_ENTRY(pos, AT_CANARYLEN, imgp->canarylen);
|
||||
|
@ -494,7 +494,7 @@ proc0_init(void *dummy __unused)
|
||||
td->td_flags = TDF_INMEM;
|
||||
td->td_pflags = TDP_KTHREAD;
|
||||
td->td_cpuset = cpuset_thread0();
|
||||
prison0.pr_cpuset = cpuset_ref(td->td_cpuset);
|
||||
prison0_init();
|
||||
p->p_peers = 0;
|
||||
p->p_leader = p;
|
||||
p->p_reaper = p;
|
||||
|
@ -239,6 +239,19 @@ static int jail_default_devfs_rsnum = JAIL_DEFAULT_DEVFS_RSNUM;
|
||||
static unsigned jail_max_af_ips = 255;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize the parts of prison0 that can't be static-initialized with
|
||||
* constants. This is called from proc0_init() after creating thread0 cpuset.
|
||||
*/
|
||||
void
|
||||
prison0_init(void)
|
||||
{
|
||||
|
||||
prison0.pr_cpuset = cpuset_ref(thread0.td_cpuset);
|
||||
prison0.pr_osreldate = osreldate;
|
||||
strlcpy(prison0.pr_osrelease, osrelease, sizeof(prison0.pr_osrelease));
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
static int
|
||||
qcmp_v4(const void *ip1, const void *ip2)
|
||||
@ -538,7 +551,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
|
||||
struct prison *pr, *deadpr, *mypr, *ppr, *tpr;
|
||||
struct vnode *root;
|
||||
char *domain, *errmsg, *host, *name, *namelc, *p, *path, *uuid;
|
||||
char *g_path;
|
||||
char *g_path, *osrelstr;
|
||||
#if defined(INET) || defined(INET6)
|
||||
struct prison *tppr;
|
||||
void *op;
|
||||
@ -548,7 +561,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
|
||||
int created, cuflags, descend, enforce, error, errmsg_len, errmsg_pos;
|
||||
int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel;
|
||||
int fi, jid, jsys, len, level;
|
||||
int childmax, rsnum, slevel;
|
||||
int childmax, osreldt, rsnum, slevel;
|
||||
int fullpath_disabled;
|
||||
#if defined(INET) || defined(INET6)
|
||||
int ii, ij;
|
||||
@ -959,6 +972,46 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
error = vfs_getopt(opts, "osrelease", (void **)&osrelstr, &len);
|
||||
if (error == ENOENT)
|
||||
osrelstr = NULL;
|
||||
else if (error != 0)
|
||||
goto done_free;
|
||||
else {
|
||||
if (flags & JAIL_UPDATE) {
|
||||
error = EINVAL;
|
||||
vfs_opterror(opts,
|
||||
"osrelease cannot be changed after creation");
|
||||
goto done_errmsg;
|
||||
}
|
||||
if (len == 0 || len >= OSRELEASELEN) {
|
||||
error = EINVAL;
|
||||
vfs_opterror(opts,
|
||||
"osrelease string must be 1-%d bytes long",
|
||||
OSRELEASELEN - 1);
|
||||
goto done_errmsg;
|
||||
}
|
||||
}
|
||||
|
||||
error = vfs_copyopt(opts, "osreldate", &osreldt, sizeof(osreldt));
|
||||
if (error == ENOENT)
|
||||
osreldt = 0;
|
||||
else if (error != 0)
|
||||
goto done_free;
|
||||
else {
|
||||
if (flags & JAIL_UPDATE) {
|
||||
error = EINVAL;
|
||||
vfs_opterror(opts,
|
||||
"osreldate cannot be changed after creation");
|
||||
goto done_errmsg;
|
||||
}
|
||||
if (osreldt == 0) {
|
||||
error = EINVAL;
|
||||
vfs_opterror(opts, "osreldate cannot be 0");
|
||||
goto done_errmsg;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Grab the allprison lock before letting modules check their
|
||||
* parameters. Once we have it, do not let go so we'll have a
|
||||
@ -1285,6 +1338,12 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
|
||||
pr->pr_enforce_statfs = JAIL_DEFAULT_ENFORCE_STATFS;
|
||||
pr->pr_devfs_rsnum = ppr->pr_devfs_rsnum;
|
||||
|
||||
pr->pr_osreldate = osreldt ? osreldt : ppr->pr_osreldate;
|
||||
if (osrelstr == NULL)
|
||||
strcpy(pr->pr_osrelease, ppr->pr_osrelease);
|
||||
else
|
||||
strcpy(pr->pr_osrelease, osrelstr);
|
||||
|
||||
LIST_INIT(&pr->pr_children);
|
||||
mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF | MTX_DUPOK);
|
||||
|
||||
@ -4303,12 +4362,20 @@ sysctl_jail_param(SYSCTL_HANDLER_ARGS)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* CTLFLAG_RDTUN in the following indicates jail parameters that can be set at
|
||||
* jail creation time but cannot be changed in an existing jail.
|
||||
*/
|
||||
SYSCTL_JAIL_PARAM(, jid, CTLTYPE_INT | CTLFLAG_RDTUN, "I", "Jail ID");
|
||||
SYSCTL_JAIL_PARAM(, parent, CTLTYPE_INT | CTLFLAG_RD, "I", "Jail parent ID");
|
||||
SYSCTL_JAIL_PARAM_STRING(, name, CTLFLAG_RW, MAXHOSTNAMELEN, "Jail name");
|
||||
SYSCTL_JAIL_PARAM_STRING(, path, CTLFLAG_RDTUN, MAXPATHLEN, "Jail root path");
|
||||
SYSCTL_JAIL_PARAM(, securelevel, CTLTYPE_INT | CTLFLAG_RW,
|
||||
"I", "Jail secure level");
|
||||
SYSCTL_JAIL_PARAM(, osreldate, CTLTYPE_INT | CTLFLAG_RDTUN, "I",
|
||||
"Jail value for kern.osreldate and uname -K");
|
||||
SYSCTL_JAIL_PARAM_STRING(, osrelease, CTLFLAG_RDTUN, OSRELEASELEN,
|
||||
"Jail value for kern.osrelease and uname -r");
|
||||
SYSCTL_JAIL_PARAM(, enforce_statfs, CTLTYPE_INT | CTLFLAG_RW,
|
||||
"I", "Jail cannot see all mounted file systems");
|
||||
SYSCTL_JAIL_PARAM(, devfs_ruleset, CTLTYPE_INT | CTLFLAG_RW,
|
||||
|
@ -90,9 +90,6 @@ SYSCTL_ROOT_NODE(OID_AUTO, regression, CTLFLAG_RW, 0,
|
||||
SYSCTL_STRING(_kern, OID_AUTO, ident, CTLFLAG_RD|CTLFLAG_MPSAFE,
|
||||
kern_ident, 0, "Kernel identifier");
|
||||
|
||||
SYSCTL_STRING(_kern, KERN_OSRELEASE, osrelease, CTLFLAG_RD|CTLFLAG_MPSAFE|
|
||||
CTLFLAG_CAPRD, osrelease, 0, "Operating system release");
|
||||
|
||||
SYSCTL_INT(_kern, KERN_OSREV, osrevision, CTLFLAG_RD|CTLFLAG_CAPRD,
|
||||
SYSCTL_NULL_INT_PTR, BSD, "Operating system revision");
|
||||
|
||||
@ -105,13 +102,6 @@ SYSCTL_STRING(_kern, OID_AUTO, compiler_version, CTLFLAG_RD|CTLFLAG_MPSAFE,
|
||||
SYSCTL_STRING(_kern, KERN_OSTYPE, ostype, CTLFLAG_RD|CTLFLAG_MPSAFE|
|
||||
CTLFLAG_CAPRD, ostype, 0, "Operating system type");
|
||||
|
||||
/*
|
||||
* NOTICE: The *userland* release date is available in
|
||||
* /usr/include/osreldate.h
|
||||
*/
|
||||
SYSCTL_INT(_kern, KERN_OSRELDATE, osreldate, CTLFLAG_RD|CTLFLAG_CAPRD,
|
||||
&osreldate, 0, "Kernel release date");
|
||||
|
||||
SYSCTL_INT(_kern, KERN_MAXPROC, maxproc, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
|
||||
&maxproc, 0, "Maximum number of processes");
|
||||
|
||||
@ -429,6 +419,48 @@ SYSCTL_PROC(_kern, KERN_HOSTID, hostid,
|
||||
CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
|
||||
NULL, 0, sysctl_hostid, "LU", "Host ID");
|
||||
|
||||
/*
|
||||
* The osrelease string is copied from the global (osrelease in vers.c) into
|
||||
* prison0 by a sysinit and is inherited by child jails if not changed at jail
|
||||
* creation, so we always return the copy from the current prison data.
|
||||
*/
|
||||
static int
|
||||
sysctl_osrelease(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct prison *pr;
|
||||
|
||||
pr = req->td->td_ucred->cr_prison;
|
||||
return (SYSCTL_OUT(req, pr->pr_osrelease, strlen(pr->pr_osrelease) + 1));
|
||||
|
||||
}
|
||||
|
||||
SYSCTL_PROC(_kern, KERN_OSRELEASE, osrelease,
|
||||
CTLTYPE_STRING | CTLFLAG_CAPRD | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
NULL, 0, sysctl_osrelease, "A", "Operating system release");
|
||||
|
||||
/*
|
||||
* The osreldate number is copied from the global (osreldate in vers.c) into
|
||||
* prison0 by a sysinit and is inherited by child jails if not changed at jail
|
||||
* creation, so we always return the value from the current prison data.
|
||||
*/
|
||||
static int
|
||||
sysctl_osreldate(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct prison *pr;
|
||||
|
||||
pr = req->td->td_ucred->cr_prison;
|
||||
return (SYSCTL_OUT(req, &pr->pr_osreldate, sizeof(pr->pr_osreldate)));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTICE: The *userland* release date is available in
|
||||
* /usr/include/osreldate.h
|
||||
*/
|
||||
SYSCTL_PROC(_kern, KERN_OSRELDATE, osreldate,
|
||||
CTLTYPE_INT | CTLFLAG_CAPRD | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
NULL, 0, sysctl_osreldate, "I", "Kernel release date");
|
||||
|
||||
SYSCTL_NODE(_kern, OID_AUTO, features, CTLFLAG_RD, 0, "Kernel Features");
|
||||
|
||||
#ifdef COMPAT_FREEBSD4
|
||||
|
@ -134,6 +134,7 @@ MALLOC_DECLARE(M_PRISON);
|
||||
#include <sys/osd.h>
|
||||
|
||||
#define HOSTUUIDLEN 64
|
||||
#define OSRELEASELEN 32
|
||||
|
||||
struct racct;
|
||||
struct prison_racct;
|
||||
@ -177,13 +178,15 @@ struct prison {
|
||||
int pr_securelevel; /* (p) securelevel */
|
||||
int pr_enforce_statfs; /* (p) statfs permission */
|
||||
int pr_devfs_rsnum; /* (p) devfs ruleset */
|
||||
int pr_spare[4];
|
||||
int pr_spare[3];
|
||||
int pr_osreldate; /* (c) kern.osreldate value */
|
||||
unsigned long pr_hostid; /* (p) jail hostid */
|
||||
char pr_name[MAXHOSTNAMELEN]; /* (p) admin jail name */
|
||||
char pr_path[MAXPATHLEN]; /* (c) chroot path */
|
||||
char pr_hostname[MAXHOSTNAMELEN]; /* (p) jail hostname */
|
||||
char pr_domainname[MAXHOSTNAMELEN]; /* (p) jail domainname */
|
||||
char pr_hostuuid[HOSTUUIDLEN]; /* (p) jail hostuuid */
|
||||
char pr_osrelease[OSRELEASELEN]; /* (c) kern.osrelease value */
|
||||
};
|
||||
|
||||
struct prison_racct {
|
||||
@ -362,6 +365,7 @@ void getcredhostname(struct ucred *, char *, size_t);
|
||||
void getcreddomainname(struct ucred *, char *, size_t);
|
||||
void getcredhostuuid(struct ucred *, char *, size_t);
|
||||
void getcredhostid(struct ucred *, unsigned long *);
|
||||
void prison0_init(void);
|
||||
int prison_allow(struct ucred *, unsigned);
|
||||
int prison_check(struct ucred *cred1, struct ucred *cred2);
|
||||
int prison_owns_vnet(struct ucred *);
|
||||
|
@ -25,7 +25,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 6, 2015
|
||||
.Dd February 25, 2015
|
||||
.Dt JAIL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -276,7 +276,7 @@ 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
|
||||
Jails have a set of 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 .
|
||||
@ -471,6 +471,14 @@ The
|
||||
.Va jid
|
||||
of the parent of this jail, or zero if this is a top-level jail
|
||||
(read-only).
|
||||
.It Va osrelease
|
||||
The string for the jail's
|
||||
.Va kern.osrelease
|
||||
sysctl and uname -r.
|
||||
.It Va osreldate
|
||||
The number for the jail's
|
||||
.Va kern.osreldate
|
||||
and uname -K.
|
||||
.It Va allow.*
|
||||
Some restrictions of the jail environment may be set on a per-jail
|
||||
basis.
|
||||
|
Loading…
Reference in New Issue
Block a user