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
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to