Add a limit for child jails via the "children.cur" and "children.max"

parameters.  This replaces the simple "allow.jails" permission.

Approved by:	bz (mentor)
This commit is contained in:
Jamie Gritton 2009-06-23 20:35:51 +00:00
parent bb8ddc66ea
commit b97457e2e6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=194762
4 changed files with 93 additions and 28 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd May 27, 2009
.Dd June 23, 2009
.Dt JAIL 2
.Os
.Sh NAME
@ -293,9 +293,9 @@ will fail if:
.Bl -tag -width Er
.It Bq Er EPERM
This process is not allowed to create a jail, either because it is not
the super-user, or because it is in a jail where the
.Va allow.jails
parameter is not set.
the super-user, or because it would exceed the jail's
.Va children.max
limit.
.It Bq Er EFAULT
.Fa jail
points to an address outside the allocated address space of the process.
@ -312,9 +312,9 @@ will fail if:
.Bl -tag -width Er
.It Bq Er EPERM
This process is not allowed to create a jail, either because it is not
the super-user, or because it is in a jail where the
.Va allow.jails
parameter is not set.
the super-user, or because it would exceed the jail's
.Va children.max
limit.
.It Bq Er EPERM
A jail parameter was set to a less restrictive value then the current
environment.

View File

@ -80,6 +80,7 @@ struct prison prison0 = {
.pr_uref = 1,
.pr_path = "/",
.pr_securelevel = -1,
.pr_childmax = JAIL_MAX,
.pr_hostuuid = "00000000-0000-0000-0000-000000000000",
.pr_children = LIST_HEAD_INITIALIZER(&prison0.pr_children),
.pr_flags = PR_HOST,
@ -152,7 +153,6 @@ static char *pr_allow_names[] = {
"allow.chflags",
"allow.mount",
"allow.quotas",
"allow.jails",
"allow.socket_af",
};
@ -163,7 +163,6 @@ static char *pr_allow_nonames[] = {
"allow.nochflags",
"allow.nomount",
"allow.noquotas",
"allow.nojails",
"allow.nosocket_af",
};
@ -479,8 +478,8 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
unsigned long hid;
size_t namelen, onamelen;
int created, cuflags, descend, enforce, error, errmsg_len, errmsg_pos;
int gotenforce, gothid, gotslevel, fi, jid, len;
int slevel, vfslocked;
int gotchildmax, gotenforce, gothid, gotslevel, fi, jid, len, level;
int childmax, slevel, vfslocked;
#if defined(INET) || defined(INET6)
int ii, ij;
#endif
@ -500,7 +499,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
if (error)
return (error);
mypr = ppr = td->td_ucred->cr_prison;
if ((flags & JAIL_CREATE) && !(mypr->pr_allow & PR_ALLOW_JAILS))
if ((flags & JAIL_CREATE) && mypr->pr_childmax == 0)
return (EPERM);
if (flags & ~JAIL_SET_MASK)
return (EINVAL);
@ -544,6 +543,15 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
else
gotslevel = 1;
error =
vfs_copyopt(opts, "children.max", &childmax, sizeof(childmax));
if (error == ENOENT)
gotchildmax = 0;
else if (error != 0)
goto done_free;
else
gotchildmax = 1;
error = vfs_copyopt(opts, "enforce_statfs", &enforce, sizeof(enforce));
gotenforce = (error == 0);
if (gotenforce) {
@ -1023,6 +1031,12 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
/* If there's no prison to update, create a new one and link it in. */
if (pr == NULL) {
for (tpr = mypr; tpr != NULL; tpr = tpr->pr_parent)
if (tpr->pr_childcount >= tpr->pr_childmax) {
error = EPERM;
vfs_opterror(opts, "prison limit exceeded");
goto done_unlock_list;
}
created = 1;
mtx_lock(&ppr->pr_mtx);
if (ppr->pr_ref == 0 || (ppr->pr_flags & PR_REMOVE)) {
@ -1076,7 +1090,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
TAILQ_INSERT_TAIL(&allprison, pr, pr_list);
LIST_INSERT_HEAD(&ppr->pr_children, pr, pr_sibling);
for (tpr = ppr; tpr != NULL; tpr = tpr->pr_parent)
tpr->pr_prisoncount++;
tpr->pr_childcount++;
pr->pr_parent = ppr;
pr->pr_id = jid;
@ -1163,6 +1177,12 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
goto done_deref_locked;
}
}
if (gotchildmax) {
if (childmax >= ppr->pr_childmax) {
error = EPERM;
goto done_deref_locked;
}
}
if (gotenforce) {
if (enforce < ppr->pr_enforce_statfs) {
error = EPERM;
@ -1506,6 +1526,14 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
if (tpr->pr_securelevel < slevel)
tpr->pr_securelevel = slevel;
}
if (gotchildmax) {
pr->pr_childmax = childmax;
/* Set all child jails to under this limit. */
FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL(pr, tpr, descend, level)
if (tpr->pr_childmax > childmax - level)
tpr->pr_childmax = childmax > level
? childmax - level : 0;
}
if (gotenforce) {
pr->pr_enforce_statfs = enforce;
/* Pass this restriction on to the children. */
@ -1895,6 +1923,14 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags)
sizeof(pr->pr_securelevel));
if (error != 0 && error != ENOENT)
goto done_deref;
error = vfs_setopt(opts, "children.cur", &pr->pr_childcount,
sizeof(pr->pr_childcount));
if (error != 0 && error != ENOENT)
goto done_deref;
error = vfs_setopt(opts, "children.max", &pr->pr_childmax,
sizeof(pr->pr_childmax));
if (error != 0 && error != ENOENT)
goto done_deref;
error = vfs_setopts(opts, "host.hostname", pr->pr_hostname);
if (error != 0 && error != ENOENT)
goto done_deref;
@ -2425,7 +2461,7 @@ prison_deref(struct prison *pr, int flags)
LIST_REMOVE(pr, pr_sibling);
ppr = pr->pr_parent;
for (tpr = ppr; tpr != NULL; tpr = tpr->pr_parent)
tpr->pr_prisoncount--;
tpr->pr_childcount--;
sx_downgrade(&allprison_lock);
#ifdef VIMAGE
@ -3878,6 +3914,12 @@ SYSCTL_JAIL_PARAM(, vnet, CTLTYPE_INT | CTLFLAG_RDTUN,
SYSCTL_JAIL_PARAM(, dying, CTLTYPE_INT | CTLFLAG_RD,
"B", "Jail is in the process of shutting down");
SYSCTL_JAIL_PARAM_NODE(children, "Number of child jails");
SYSCTL_JAIL_PARAM(_children, cur, CTLTYPE_INT | CTLFLAG_RD,
"I", "Current number of child jails");
SYSCTL_JAIL_PARAM(_children, max, CTLTYPE_INT | CTLFLAG_RW,
"I", "Maximum number of child jails");
SYSCTL_JAIL_PARAM_NODE(host, "Jail host info");
SYSCTL_JAIL_PARAM(, nohost, CTLTYPE_INT | CTLFLAG_RW,
"BN", "Jail w/ no host info");
@ -3921,8 +3963,6 @@ SYSCTL_JAIL_PARAM(_allow, mount, CTLTYPE_INT | CTLFLAG_RW,
"B", "Jail may mount/unmount jail-friendly file systems");
SYSCTL_JAIL_PARAM(_allow, quotas, CTLTYPE_INT | CTLFLAG_RW,
"B", "Jail may set file quotas");
SYSCTL_JAIL_PARAM(_allow, jails, CTLTYPE_INT | CTLFLAG_RW,
"B", "Jail may create child jails");
SYSCTL_JAIL_PARAM(_allow, socket_af, CTLTYPE_INT | CTLFLAG_RW,
"B", "Jail may create sockets other than just UNIX/IPv4/IPv6/route");
@ -3954,6 +3994,7 @@ db_show_prison(struct prison *pr)
#endif
db_printf(" root = %p\n", pr->pr_root);
db_printf(" securelevel = %d\n", pr->pr_securelevel);
db_printf(" childcount = %d\n", pr->pr_childcount);
db_printf(" child = %p\n", LIST_FIRST(&pr->pr_children));
db_printf(" sibling = %p\n", LIST_NEXT(pr, pr_sibling));
db_printf(" flags = %x", pr->pr_flags);

View File

@ -165,13 +165,14 @@ struct prison {
struct in6_addr *pr_ip6; /* (p) v6 IPs of jail */
LIST_HEAD(, prison) pr_children; /* (a) list of child jails */
LIST_ENTRY(prison) pr_sibling; /* (a) next in parent's list */
int pr_prisoncount; /* (a) number of child jails */
int pr_childcount; /* (a) number of child jails */
unsigned pr_allow; /* (p) PR_ALLOW_* flags */
int pr_enforce_statfs; /* (p) statfs permission */
char pr_domainname[MAXHOSTNAMELEN]; /* (p) jail domainname */
char pr_hostuuid[HOSTUUIDLEN]; /* (p) jail hostuuid */
unsigned long pr_hostid; /* (p) jail hostid */
struct vnet *pr_vnet; /* (c) network stack */
int pr_childmax; /* (p) maximum child jails */
};
#endif /* _KERNEL || _WANT_PRISON */
@ -197,9 +198,8 @@ struct prison {
#define PR_ALLOW_CHFLAGS 0x0008
#define PR_ALLOW_MOUNT 0x0010
#define PR_ALLOW_QUOTAS 0x0020
#define PR_ALLOW_JAILS 0x0040
#define PR_ALLOW_SOCKET_AF 0x0080
#define PR_ALLOW_ALL 0x00ff
#define PR_ALLOW_SOCKET_AF 0x0040
#define PR_ALLOW_ALL 0x007f
/*
* OSD methods
@ -270,6 +270,23 @@ prison_unlock(struct prison *pr)
; \
else
/*
* As above, but also keep track of the level descended to.
*/
#define FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL(ppr, cpr, descend, level)\
for ((cpr) = (ppr), (descend) = 1, (level) = 0; \
((cpr) = (((descend) && !LIST_EMPTY(&(cpr)->pr_children)) \
? (level++, LIST_FIRST(&(cpr)->pr_children)) \
: ((cpr) == (ppr) \
? NULL \
: ((prison_unlock(cpr), \
(descend) = LIST_NEXT(cpr, pr_sibling) != NULL) \
? LIST_NEXT(cpr, pr_sibling) \
: (level--, (cpr)->pr_parent)))));) \
if ((descend) ? (prison_lock(cpr), 0) : 1) \
; \
else
/*
* Attributes of the physical system, and the root of the jail tree.
*/

View File

@ -34,7 +34,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd May 29, 2009
.Dd June 23, 2009
.Dt JAIL 8
.Os
.Sh NAME
@ -279,6 +279,17 @@ A jail never has a lower securelevel than the default system, but by
setting this parameter it may have a higher one.
If the system securelevel is changed, any jail securelevels will be at
least as secure.
.It Va 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"
section for more information.
.It Va children.cur
The number of descendents of this jail, including its own child jails
and any jails created under them.
.It Va enforce_statfs
This determines which information processes in a jail are able to get
about mount points.
@ -368,10 +379,6 @@ with non-jailed parts of the system.
Sockets within a jail are normally restricted to IPv4, IPv6, local
(UNIX), and route. This allows access to other protocol stacks that
have not had jail functionality added to them.
.It Va allow.jails
The prison root may create child jails under this jail. See the
.Va "Hierarchical Jails"
section for more information.
.El
.El
.Pp
@ -756,7 +763,7 @@ and
.Va kern.hostuuid .
.Ss "Hierarchical Jails"
By setting a jail's
.Va allow.jails
.Va children.max
parameter, processes within a jail may be able to create jails of their own.
These child jails are kept in a hierarchy, with jails only able to see and/or
modify the jails they created (or those jails' children).
@ -782,8 +789,8 @@ and
may not be bypassed in child jails.
.Pp
A child jail may in turn create its own child jails if its own
.Va allow.jails
parameter is set (remember it is off by default).
.Va children.max
parameter is set (remember it is zero by default).
These jails are visible to and can be modified by their parent and all
ancestors.
.Pp