From bf3db8aa6581ef04796d719aa54df39e3915617e Mon Sep 17 00:00:00 2001 From: Martin Matuska Date: Thu, 23 Feb 2012 18:51:24 +0000 Subject: [PATCH] To improve control over the use of mount(8) inside a jail(8), introduce a new jail parameter node with the following parameters: allow.mount.devfs: allow mounting the devfs filesystem inside a jail allow.mount.nullfs: allow mounting the nullfs filesystem inside a jail Both parameters are disabled by default (equals the behavior before devfs and nullfs in jails). Administrators have to explicitly allow mounting devfs and nullfs for each jail. The value "-1" of the devfs_ruleset parameter is removed in favor of the new allow setting. Reviewed by: jamie Suggested by: pjd MFC after: 2 weeks --- sys/fs/devfs/devfs_vfsops.c | 30 +++++++++++++++--------------- sys/fs/nullfs/null_vfsops.c | 5 +++++ sys/kern/kern_jail.c | 37 ++++++++++++++++++++++++++----------- sys/sys/jail.h | 6 +++++- usr.sbin/jail/jail.8 | 36 ++++++++++++++++++++++++++++-------- 5 files changed, 79 insertions(+), 35 deletions(-) diff --git a/sys/fs/devfs/devfs_vfsops.c b/sys/fs/devfs/devfs_vfsops.c index 96069747e39e..f73b229efa81 100644 --- a/sys/fs/devfs/devfs_vfsops.c +++ b/sys/fs/devfs/devfs_vfsops.c @@ -71,7 +71,7 @@ devfs_mount(struct mount *mp) struct devfs_mount *fmp; struct vnode *rvp; struct thread *td = curthread; - int rsnum; + int injail, rsnum; if (devfs_unr == NULL) devfs_unr = new_unrhdr(0, INT_MAX, NULL); @@ -81,7 +81,11 @@ devfs_mount(struct mount *mp) if (mp->mnt_flag & MNT_ROOTFS) return (EOPNOTSUPP); + if (!prison_allow(td->td_ucred, PR_ALLOW_MOUNT_DEVFS)) + return (EPERM); + rsnum = 0; + injail = jailed(td->td_ucred); if (mp->mnt_optnew != NULL) { if (vfs_filteropt(mp->mnt_optnew, devfs_opts)) @@ -89,24 +93,20 @@ devfs_mount(struct mount *mp) 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; - } + &rsnum) != 1 || rsnum < 0 || rsnum > 65535)) { + vfs_mount_error(mp, "%s", + "invalid ruleset specification"); + return (EINVAL); + } - /* jails enforce their ruleset, prison0 has no restrictions */ - if (td->td_ucred->cr_prison->pr_devfs_rsnum != 0) { - rsnum = td->td_ucred->cr_prison->pr_devfs_rsnum; - if (rsnum == -1) + if (injail && rsnum != 0 && + rsnum != td->td_ucred->cr_prison->pr_devfs_rsnum) 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); - } + /* jails enforce their ruleset */ + if (injail) + rsnum = td->td_ucred->cr_prison->pr_devfs_rsnum; if (mp->mnt_flag & MNT_UPDATE) { if (rsnum != 0) { diff --git a/sys/fs/nullfs/null_vfsops.c b/sys/fs/nullfs/null_vfsops.c index 97874b3e0dea..b422077f0cf0 100644 --- a/sys/fs/nullfs/null_vfsops.c +++ b/sys/fs/nullfs/null_vfsops.c @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -75,12 +76,16 @@ nullfs_mount(struct mount *mp) struct vnode *lowerrootvp, *vp; struct vnode *nullm_rootvp; struct null_mount *xmp; + struct thread *td = curthread; char *target; int isvnunlocked = 0, len; struct nameidata nd, *ndp = &nd; NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); + if (!prison_allow(td->td_ucred, PR_ALLOW_MOUNT_NULLFS)) + return (EPERM); + if (mp->mnt_flag & MNT_ROOTFS) return (EOPNOTSUPP); /* diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index b800eaa29089..3ba565b55892 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -201,6 +201,8 @@ static char *pr_allow_names[] = { "allow.mount", "allow.quotas", "allow.socket_af", + "allow.mount.devfs", + "allow.mount.nullfs", }; const size_t pr_allow_names_size = sizeof(pr_allow_names); @@ -212,12 +214,14 @@ static char *pr_allow_nonames[] = { "allow.nomount", "allow.noquotas", "allow.nosocket_af", + "allow.mount.nodevfs", + "allow.mount.nonullfs", }; const size_t pr_allow_nonames_size = sizeof(pr_allow_nonames); #define JAIL_DEFAULT_ALLOW PR_ALLOW_SET_HOSTNAME #define JAIL_DEFAULT_ENFORCE_STATFS 2 -#define JAIL_DEFAULT_DEVFS_RSNUM -1 +#define JAIL_DEFAULT_DEVFS_RSNUM 0 static unsigned jail_default_allow = JAIL_DEFAULT_ALLOW; static int jail_default_enforce_statfs = JAIL_DEFAULT_ENFORCE_STATFS; static int jail_default_devfs_rsnum = JAIL_DEFAULT_DEVFS_RSNUM; @@ -1279,7 +1283,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) pr->pr_securelevel = ppr->pr_securelevel; pr->pr_allow = JAIL_DEFAULT_ALLOW & ppr->pr_allow; pr->pr_enforce_statfs = JAIL_DEFAULT_ENFORCE_STATFS; - pr->pr_devfs_rsnum = JAIL_DEFAULT_DEVFS_RSNUM; + pr->pr_devfs_rsnum = ppr->pr_devfs_rsnum; LIST_INIT(&pr->pr_children); mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF | MTX_DUPOK); @@ -1361,21 +1365,19 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) if (gotrsnum) { /* * devfs_rsnum is a uint16_t - * value of -1 disables devfs mounts */ - if (rsnum < -1 || rsnum > 65535) { + if (rsnum < 0 || rsnum > 65535) { error = EINVAL; goto done_deref_locked; } /* - * Nested jails may inherit parent's devfs ruleset - * or disable devfs + * Nested jails always inherit parent's devfs ruleset */ if (jailed(td->td_ucred)) { if (rsnum > 0 && rsnum != ppr->pr_devfs_rsnum) { error = EPERM; goto done_deref_locked; - } else if (rsnum == 0) + } else rsnum = ppr->pr_devfs_rsnum; } } @@ -1623,8 +1625,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) 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; + tpr->pr_devfs_rsnum = rsnum; } if (name != NULL) { if (ppr == &prison0) @@ -4195,6 +4196,14 @@ SYSCTL_PROC(_security_jail, OID_AUTO, mount_allowed, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, PR_ALLOW_MOUNT, sysctl_jail_default_allow, "I", "Processes in jail can mount/unmount jail-friendly file systems"); +SYSCTL_PROC(_security_jail, OID_AUTO, mount_devfs_allowed, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + NULL, PR_ALLOW_MOUNT_DEVFS, sysctl_jail_default_allow, "I", + "Processes in jail can mount/unmount the devfs file system"); +SYSCTL_PROC(_security_jail, OID_AUTO, mount_nullfs_allowed, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + NULL, PR_ALLOW_MOUNT_NULLFS, sysctl_jail_default_allow, "I", + "Processes in jail can mount/unmount the nullfs file system"); static int sysctl_jail_default_level(SYSCTL_HANDLER_ARGS) @@ -4329,13 +4338,19 @@ SYSCTL_JAIL_PARAM(_allow, raw_sockets, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail may create raw sockets"); SYSCTL_JAIL_PARAM(_allow, chflags, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail may alter system file flags"); -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, socket_af, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail may create sockets other than just UNIX/IPv4/IPv6/route"); +SYSCTL_JAIL_PARAM_SUBNODE(allow, mount, "Jail mount/unmount permission flags"); +SYSCTL_JAIL_PARAM(_allow_mount, , CTLTYPE_INT | CTLFLAG_RW, + "B", "Jail may mount/unmount jail-friendly file systems in general"); +SYSCTL_JAIL_PARAM(_allow_mount, devfs, CTLTYPE_INT | CTLFLAG_RW, + "B", "Jail may mount/unmount the devfs file system"); +SYSCTL_JAIL_PARAM(_allow_mount, nullfs, CTLTYPE_INT | CTLFLAG_RW, + "B", "Jail may mount/unmount the nullfs file system"); + void prison_racct_foreach(void (*callback)(struct racct *racct, void *arg2, void *arg3), void *arg2, void *arg3) diff --git a/sys/sys/jail.h b/sys/sys/jail.h index 565efa86dccd..7d87b84bc0d4 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -223,7 +223,9 @@ struct prison_racct { #define PR_ALLOW_MOUNT 0x0010 #define PR_ALLOW_QUOTAS 0x0020 #define PR_ALLOW_SOCKET_AF 0x0040 -#define PR_ALLOW_ALL 0x007f +#define PR_ALLOW_MOUNT_DEVFS 0x0080 +#define PR_ALLOW_MOUNT_NULLFS 0x0100 +#define PR_ALLOW_ALL 0x01ff /* * OSD methods @@ -338,6 +340,8 @@ SYSCTL_DECL(_security_jail_param); sysctl_jail_param, fmt, descr) #define SYSCTL_JAIL_PARAM_NODE(module, descr) \ SYSCTL_NODE(_security_jail_param, OID_AUTO, module, 0, 0, descr) +#define SYSCTL_JAIL_PARAM_SUBNODE(parent, module, descr) \ + SYSCTL_NODE(_security_jail_param_##parent, OID_AUTO, module, 0, 0, descr) #define SYSCTL_JAIL_PARAM_SYS_NODE(module, access, descr) \ SYSCTL_JAIL_PARAM_NODE(module, descr); \ SYSCTL_JAIL_PARAM(_##module, , CTLTYPE_INT | (access), "E,jailsys", \ diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8 index 5cd77fc705c3..e1d3b2d2b79c 100644 --- a/usr.sbin/jail/jail.8 +++ b/usr.sbin/jail/jail.8 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 9, 2012 +.Dd February 23, 2012 .Dt JAIL 8 .Os .Sh NAME @@ -303,15 +303,16 @@ 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 (default) means mounting a -devfs filesystem is not allowed. Mounting devfs inside a jail is possible -only if the +this jail. A value of zero (default) means no ruleset is enforced. Descendant +jails inherit the parent jail's devfs ruleset enforcement. Mounting devfs +inside a jail is possible only if the .Va allow.mount -permission is effective and +and +.Va allow.mount.devfs +permissions are effective and .Va enforce_statfs -is set to a value lower than 2. +is set to a value lower than 2. Devfs rules and rulesets cannot be viewed or +modified from inside a jail. .It Va children.max The number of child jails allowed to be created by this jail (or by other jails under this jail). @@ -407,6 +408,25 @@ within a jail. This permission is effective only if .Va enforce_statfs is set to a value lower than 2. +.It Va allow.mount.devfs +privileged users inside the jail will be able to mount and unmount the +devfs file system. +This permission is effective only together with +.Va allow.mount +and if +.Va enforce_statfs +is set to a value lower than 2. Please consider restricting the devfs ruleset +with the +.Va devfs_ruleset +option. +.It Va allow.mount.nullfs +privileged users inside the jail will be able to mount and unmount the +nullfs file system. +This permission is effective only together with +.Va allow.mount +and if +.Va enforce_statfs +is set to a value lower than 2. .It Va allow.quotas The prison root may administer quotas on the jail's filesystem(s). This includes filesystems that the jail may share with other jails or