Index: share/man/man5/devfs.5 =================================================================== --- share/man/man5/devfs.5 (revision 231134) +++ share/man/man5/devfs.5 (working copy) @@ -90,6 +90,29 @@ and .Pa 2 . .Xr fdescfs 5 creates files for all open descriptors. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o Ar options +Use the specified mount +.Ar options , +as described in +.Xr mount 8 . +The following devfs file system-specific options are available: +.Bl -tag -width indent +.It Cm ruleset Ns No = Ns Ar ruleset +Set ruleset number +.Ar ruleset +as the current ruleset for the mount-point and apply all its rules. If the +ruleset number +.Ar ruleset +does not exist, an empty ruleset with the number +.Ar ruleset +is created. See +.Xr devfs 8 +for more information on working with devfs rulesets. +.El +.El .Sh FILES .Bl -tag -width /dev/XXXX -compact .It Pa /dev Index: usr.sbin/jail/jail.8 =================================================================== --- usr.sbin/jail/jail.8 (revision 231134) +++ usr.sbin/jail/jail.8 (working copy) @@ -301,6 +301,16 @@ 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 mounting devfs in +this jail and its descendants. A value of zero means no ruleset is enforced or +if set inside a jail for a descendant jail, the parent jails's devfs ruleset +enforcement is inherited. A value of -1 means mounting a devfs filesystem is +not allowed. 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). Index: sys/fs/devfs/devfs_rule.c =================================================================== --- sys/fs/devfs/devfs_rule.c (revision 231134) +++ sys/fs/devfs/devfs_rule.c (working copy) @@ -771,3 +771,38 @@ 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); +} + +/* + * Apply the current active ruleset on a mount + */ +void +devfs_ruleset_apply(struct devfs_mount *dm) +{ + struct devfs_ruleset *ds; + + sx_assert(&dm->dm_lock, SX_XLOCKED); + + sx_xlock(&sx_rules); + if (dm->dm_ruleset == 0) { + sx_xunlock(&sx_rules); + return; + } + ds = devfs_ruleset_bynum(dm->dm_ruleset); + if (ds != NULL) + devfs_ruleset_applydm(ds, dm); + sx_xunlock(&sx_rules); +} Index: sys/fs/devfs/devfs_vfsops.c =================================================================== --- sys/fs/devfs/devfs_vfsops.c (revision 231134) +++ sys/fs/devfs/devfs_vfsops.c (working copy) @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -56,6 +57,10 @@ static vfs_unmount_t devfs_unmount; static vfs_root_t devfs_root; static vfs_statfs_t devfs_statfs; +static const char *devfs_opts[] = { + "from", "ruleset", NULL +}; + /* * Mount the filesystem */ @@ -65,15 +70,57 @@ devfs_mount(struct mount *mp) int error; struct devfs_mount *fmp; struct vnode *rvp; + struct thread *td = curthread; + int rsnum; if (devfs_unr == NULL) devfs_unr = new_unrhdr(0, INT_MAX, NULL); error = 0; - if (mp->mnt_flag & (MNT_UPDATE | MNT_ROOTFS)) + if (mp->mnt_flag & MNT_ROOTFS) return (EOPNOTSUPP); + rsnum = 0; + + if (mp->mnt_optnew != NULL) { + if (vfs_filteropt(mp->mnt_optnew, devfs_opts)) + return (EINVAL); + + if (vfs_getopt(mp->mnt_optnew, "ruleset", NULL, NULL) == 0 && + (vfs_scanopt(mp->mnt_optnew, "ruleset", "%d", + &rsnum) != 1 || rsnum < 0 || rsnum > 65535)) + error = EINVAL; + } + + /* jails enforce their ruleset */ + if (jailed(td->td_ucred)) { + rsnum = td->td_ucred->cr_prison->pr_devfs_rsnum; + if (rsnum == -1) + return (EPERM); + /* check rsnum for sanity, devfs_rsnum is uint16_t */ + if (rsnum < 0 || rsnum > 65535) + error = EINVAL; + } + + if (error) { + vfs_mount_error(mp, "%s", "invalid ruleset specification"); + return (error); + } + + if (mp->mnt_flag & MNT_UPDATE) { + if (rsnum != 0) { + fmp = mp->mnt_data; + if (fmp != NULL) { + sx_xlock(&fmp->dm_lock); + devfs_ruleset_set((devfs_rsnum)rsnum, fmp); + devfs_ruleset_apply(fmp); + sx_xunlock(&fmp->dm_lock); + } + } + return (0); + } + 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 +148,12 @@ devfs_mount(struct mount *mp) return (error); } + if (rsnum != 0) { + sx_xlock(&fmp->dm_lock); + devfs_ruleset_set((devfs_rsnum)rsnum, fmp); + sx_xunlock(&fmp->dm_lock); + } + VOP_UNLOCK(rvp, 0); vfs_mountedfrom(mp, "devfs"); @@ -186,4 +239,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 231134) +++ sys/fs/devfs/devfs.h (working copy) @@ -182,6 +182,8 @@ 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); +void devfs_ruleset_apply(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/sys/jail.h =================================================================== --- sys/sys/jail.h (revision 231134) +++ sys/sys/jail.h (working copy) @@ -176,7 +176,8 @@ struct prison { unsigned pr_allow; /* (p) PR_ALLOW_* flags */ int pr_securelevel; /* (p) securelevel */ int pr_enforce_statfs; /* (p) statfs permission */ - int pr_spare[5]; + int pr_devfs_rsnum; /* (p) devfs ruleset */ + int pr_spare[4]; unsigned long pr_hostid; /* (p) jail hostid */ char pr_name[MAXHOSTNAMELEN]; /* (p) admin jail name */ char pr_path[MAXPATHLEN]; /* (c) chroot path */ Index: sys/kern/kern_jail.c =================================================================== --- sys/kern/kern_jail.c (revision 231134) +++ 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 = -1, .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,27 @@ kern_jail_set(struct thread *td, struct uio *optui goto done_deref_locked; } } + if (gotrsnum) { + /* + * devfs_rsnum is a uint16_t + * value of -1 disables devfs mounts + */ + if (rsnum < -1 || rsnum > 65535) { + error = EINVAL; + goto done_deref_locked; + } + /* + * Nested jails may inherit parent's devfs ruleset + * or disallow devfs only + */ + if (jailed(td->td_ucred) { + if (rsnum > 0) { + error = EPERM; + goto done_deref_locked; + } else if (rsnum == 0) + rsnum = ppr->pr_devfs_rsnum; + } + } #ifdef INET if (ip4s > 0) { if (ppr->pr_flags & PR_IP4) { @@ -1586,6 +1616,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 != -1) + tpr->pr_devfs_rsnum = rsnum; + } if (name != NULL) { if (ppr == &prison0) strlcpy(pr->pr_name, name, sizeof(pr->pr_name)); @@ -2020,6 +2057,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 +4262,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 +4456,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));