svn commit: r194762 - in head: lib/libc/sys sys/kern sys/sys usr.sbin/jail

Jamie Gritton jamie at FreeBSD.org
Tue Jun 23 20:35:51 UTC 2009


Author: jamie
Date: Tue Jun 23 20:35:51 2009
New Revision: 194762
URL: http://svn.freebsd.org/changeset/base/194762

Log:
  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)

Modified:
  head/lib/libc/sys/jail.2
  head/sys/kern/kern_jail.c
  head/sys/sys/jail.h
  head/usr.sbin/jail/jail.8

Modified: head/lib/libc/sys/jail.2
==============================================================================
--- head/lib/libc/sys/jail.2	Tue Jun 23 20:22:34 2009	(r194761)
+++ head/lib/libc/sys/jail.2	Tue Jun 23 20:35:51 2009	(r194762)
@@ -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.

Modified: head/sys/kern/kern_jail.c
==============================================================================
--- head/sys/kern/kern_jail.c	Tue Jun 23 20:22:34 2009	(r194761)
+++ head/sys/kern/kern_jail.c	Tue Jun 23 20:35:51 2009	(r194762)
@@ -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 
 	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 
 	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 
 	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 
 
 	/* 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 
 			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 
 			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 
 			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 
 	    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 flag
 		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 | 
 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
     "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);

Modified: head/sys/sys/jail.h
==============================================================================
--- head/sys/sys/jail.h	Tue Jun 23 20:22:34 2009	(r194761)
+++ head/sys/sys/jail.h	Tue Jun 23 20:35:51 2009	(r194762)
@@ -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
@@ -271,6 +271,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.
  */
 extern struct	prison prison0;

Modified: head/usr.sbin/jail/jail.8
==============================================================================
--- head/usr.sbin/jail/jail.8	Tue Jun 23 20:22:34 2009	(r194761)
+++ head/usr.sbin/jail/jail.8	Tue Jun 23 20:35:51 2009	(r194762)
@@ -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 tha
 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


More information about the svn-src-head mailing list