Index: sys/fs/devfs/devfs_rule.c =================================================================== --- sys/fs/devfs/devfs_rule.c (revision 231065) +++ sys/fs/devfs/devfs_rule.c (working copy) @@ -771,3 +771,17 @@ devfs_rules_cleanup(struct devfs_mount *dm) devfs_ruleset_reap(ds); } } + +/* + * Make rsnum the active ruleset for dm (locked) + */ +void +devfs_ruleset_set(devfs_rsnum rsnum, struct devfs_mount *dm) +{ + + sx_assert(&dm->dm_lock, SX_XLOCKED); + + sx_xlock(&sx_rules); + devfs_ruleset_use(rsnum, dm); + sx_xunlock(&sx_rules); +} Index: sys/fs/devfs/devfs_vfsops.c =================================================================== --- sys/fs/devfs/devfs_vfsops.c (revision 231065) +++ sys/fs/devfs/devfs_vfsops.c (working copy) @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -65,6 +66,8 @@ devfs_mount(struct mount *mp) int error; struct devfs_mount *fmp; struct vnode *rvp; + struct thread *td = curthread; + devfs_rsnum rsnum; if (devfs_unr == NULL) devfs_unr = new_unrhdr(0, INT_MAX, NULL); @@ -74,6 +77,14 @@ devfs_mount(struct mount *mp) if (mp->mnt_flag & (MNT_UPDATE | MNT_ROOTFS)) return (EOPNOTSUPP); + rsnum = 0; + + if (jailed(td->td_ucred)) { + rsnum = td->td_ucred->cr_prison->pr_devfs_rsnum; + if (rsnum == 0) + return (EPERM); + } + fmp = malloc(sizeof *fmp, M_DEVFS, M_WAITOK | M_ZERO); fmp->dm_idx = alloc_unr(devfs_unr); sx_init(&fmp->dm_lock, "devfsmount"); @@ -101,6 +112,12 @@ devfs_mount(struct mount *mp) return (error); } + if (rsnum != 0) { + sx_xlock(&fmp->dm_lock); + devfs_ruleset_set(rsnum, fmp); + sx_xunlock(&fmp->dm_lock); + } + VOP_UNLOCK(rvp, 0); vfs_mountedfrom(mp, "devfs"); @@ -186,4 +203,4 @@ static struct vfsops devfs_vfsops = { .vfs_unmount = devfs_unmount, }; -VFS_SET(devfs_vfsops, devfs, VFCF_SYNTHETIC); +VFS_SET(devfs_vfsops, devfs, VFCF_SYNTHETIC | VFCF_JAIL); Index: sys/fs/devfs/devfs.h =================================================================== --- sys/fs/devfs/devfs.h (revision 231065) +++ sys/fs/devfs/devfs.h (working copy) @@ -182,6 +182,7 @@ void devfs_rules_apply(struct devfs_mount *, struc void devfs_rules_cleanup(struct devfs_mount *); int devfs_rules_ioctl(struct devfs_mount *, u_long, caddr_t, struct thread *); +void devfs_ruleset_set(devfs_rsnum rsnum, struct devfs_mount *dm); int devfs_allocv(struct devfs_dirent *, struct mount *, int, struct vnode **); char *devfs_fqpn(char *, struct devfs_mount *, struct devfs_dirent *, Index: sys/kern/kern_jail.c =================================================================== --- sys/kern/kern_jail.c (revision 231065) +++ sys/kern/kern_jail.c (working copy) @@ -103,6 +103,7 @@ struct prison prison0 = { .pr_uref = 1, .pr_path = "/", .pr_securelevel = -1, + .pr_devfs_rsnum = 0, .pr_childmax = JAIL_MAX, .pr_hostuuid = DEFAULT_HOSTUUID, .pr_children = LIST_HEAD_INITIALIZER(prison0.pr_children), @@ -529,9 +530,9 @@ kern_jail_set(struct thread *td, struct uio *optui unsigned long hid; size_t namelen, onamelen; int created, cuflags, descend, enforce, error, errmsg_len, errmsg_pos; - int gotchildmax, gotenforce, gothid, gotslevel; + int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel; int fi, jid, jsys, len, level; - int childmax, slevel, vfslocked; + int childmax, rsnum, slevel, vfslocked; int fullpath_disabled; #if defined(INET) || defined(INET6) int ii, ij; @@ -612,6 +613,14 @@ kern_jail_set(struct thread *td, struct uio *optui } else gotenforce = 1; + error = vfs_copyopt(opts, "devfs_ruleset", &rsnum, sizeof(rsnum)); + if (error == ENOENT) + gotrsnum = 0; + else if (error != 0) + goto done_free; + else + gotrsnum = 1; + pr_flags = ch_flags = 0; for (fi = 0; fi < sizeof(pr_flag_names) / sizeof(pr_flag_names[0]); fi++) { @@ -1346,6 +1355,16 @@ kern_jail_set(struct thread *td, struct uio *optui goto done_deref_locked; } } + if (gotrsnum) { + if (jailed(td->td_ucred)) { + /* + * Nested jails inherit parent's devfs ruleset + * or may disallow devfs + */ + if (rsnum != 0 && rsnum != ppr->pr_devfs_rsnum) + rsnum = ppr->pr_devfs_rsnum; + } + } #ifdef INET if (ip4s > 0) { if (ppr->pr_flags & PR_IP4) { @@ -1586,6 +1605,13 @@ kern_jail_set(struct thread *td, struct uio *optui if (tpr->pr_enforce_statfs < enforce) tpr->pr_enforce_statfs = enforce; } + if (gotrsnum) { + pr->pr_devfs_rsnum = rsnum; + /* Pass this restriction on to the children. */ + FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) + if (tpr->pr_devfs_rsnum != 0) + tpr->pr_devfs_rsnum = rsnum; + } if (name != NULL) { if (ppr == &prison0) strlcpy(pr->pr_name, name, sizeof(pr->pr_name)); @@ -2020,6 +2046,10 @@ kern_jail_get(struct thread *td, struct uio *optui sizeof(pr->pr_enforce_statfs)); if (error != 0 && error != ENOENT) goto done_deref; + error = vfs_setopt(opts, "devfs_ruleset", &pr->pr_devfs_rsnum, + sizeof(pr->pr_devfs_rsnum)); + if (error != 0 && error != ENOENT) + goto done_deref; for (fi = 0; fi < sizeof(pr_flag_names) / sizeof(pr_flag_names[0]); fi++) { if (pr_flag_names[fi] == NULL) @@ -4221,6 +4251,8 @@ SYSCTL_JAIL_PARAM(, securelevel, CTLTYPE_INT | CTL "I", "Jail secure level"); 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, + "I", "Ruleset for in-jail devfs mounts"); SYSCTL_JAIL_PARAM(, persist, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail persistence"); #ifdef VIMAGE @@ -4413,6 +4445,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(" devfs_rsnum = %d\n", pr->pr_devfs_rsnum); db_printf(" children.max = %d\n", pr->pr_childmax); db_printf(" children.cur = %d\n", pr->pr_childcount); db_printf(" child = %p\n", LIST_FIRST(&pr->pr_children)); Index: sys/sys/jail.h =================================================================== --- sys/sys/jail.h (revision 231065) +++ sys/sys/jail.h (working copy) @@ -175,6 +175,7 @@ struct prison { int pr_childmax; /* (p) maximum child jails */ unsigned pr_allow; /* (p) PR_ALLOW_* flags */ int pr_securelevel; /* (p) securelevel */ + int pr_devfs_rsnum; /* (p) enforced devfs ruleset */ int pr_enforce_statfs; /* (p) statfs permission */ int pr_spare[5]; unsigned long pr_hostid; /* (p) jail hostid */ Index: usr.sbin/jail/jail.8 =================================================================== --- usr.sbin/jail/jail.8 (revision 231065) +++ usr.sbin/jail/jail.8 (working copy) @@ -301,6 +301,15 @@ A jail never has a lower securelevel than the defa 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 devfs_ruleset +The number of the devfs ruleset that is enforced for this jail's and its +children's devfs mounts. A value of zero means mounting a devfs filesystem +is not allowed from inside a jail. Mounting devfs inside a jail is possible +only if the +.Va allow.mount +permission is effective and +.Va enforce_statfs +is set to a value lower than 2. .It Va children.max The number of child jails allowed to be created by this jail (or by other jails under this jail).