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:
Ian Lepore 2015-02-27 16:28:55 +00:00
parent 2d1731b857
commit b96bd95b85
6 changed files with 130 additions and 17 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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 *);

View File

@ -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.