Index: sys/fs/nullfs/null_vfsops.c =================================================================== --- sys/fs/nullfs/null_vfsops.c (revision 224527) +++ sys/fs/nullfs/null_vfsops.c (working copy) @@ -50,6 +50,8 @@ #include #include #include +#include +#include #include @@ -75,9 +77,10 @@ struct vnode *lowerrootvp, *vp; struct vnode *nullm_rootvp; struct null_mount *xmp; - char *target; + char *target, *fspath, *fbuf; int isvnunlocked = 0, len; struct nameidata nd, *ndp = &nd; + struct thread *td = curthread; NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); @@ -133,12 +136,28 @@ lowerrootvp = ndp->ni_vp; /* + * Resolve global filesystem path from lowerrootvp + */ + error = vn_fullpath_global(td, lowerrootvp, &fspath, &fbuf); + if (error != 0) { + vput(lowerrootvp); + return (error); + } + if (strlen(fspath) >= MNAMELEN) { + NULLFSDEBUG("f_mntfromname >= MNAMELEN\n"); + vput(lowerrootvp); + error = ENAMETOOLONG; + goto out; + } + + /* * Check multi null mount to avoid `lock against myself' panic. */ if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) { NULLFSDEBUG("nullfs_mount: multi null mount?\n"); vput(lowerrootvp); - return (EDEADLK); + error = EDEADLK; + goto out; } xmp = (struct null_mount *) malloc(sizeof(struct null_mount), @@ -161,7 +180,7 @@ VOP_UNLOCK(vp, 0); vrele(lowerrootvp); free(xmp, M_NULLFSMNT); /* XXX */ - return (error); + goto out; } /* @@ -188,11 +207,13 @@ mp->mnt_data = xmp; vfs_getnewfsid(mp); - vfs_mountedfrom(mp, target); + vfs_mountedfrom(mp, fspath); NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); - return (0); +out: + free(fbuf, M_TEMP); + return (error); } /* @@ -263,13 +284,97 @@ return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg); } +/* + * Determine whether the subject represented by cred can "see" + * source of a nullfs mount point. + * Returns: 0 for permitted, ENOENT otherwise. + */ static int +nullfs_prison_canseemount(struct ucred *cred, struct statfs *sp) +{ + struct prison *pr; + size_t len; + + pr = cred->cr_prison; + if (pr->pr_enforce_statfs == 0) + return (0); + if (pr->pr_enforce_statfs == 2) + return (ENOENT); + /* + * If jail's chroot directory is set to "/" we should be able to see + * all mount-points from inside a jail. + * This is ugly check, but this is the only situation when jail's + * directory ends with '/'. + */ + if (strcmp(pr->pr_path, "/") == 0) + return (0); + len = strlen(pr->pr_path); + if (strncmp(pr->pr_path, sp->f_mntfromname, len) != 0) + return (ENOENT); + /* + * Be sure that we don't have situation where jail's root directory + * is "/some/path" and mount point is "/some/pathpath". + */ + if (sp->f_mntfromname[len] != '\0' && sp->f_mntfromname[len] != '/') + return (ENOENT); + return (0); +} + +static void +nullfs_prison_enforce_statfs(struct ucred *cred, struct statfs *sp) +{ + char jpath[MAXPATHLEN]; + struct prison *pr; + size_t len; + + pr = cred->cr_prison; + if (pr->pr_enforce_statfs == 0) + return; + if (nullfs_prison_canseemount(cred, sp) != 0) { + bzero(sp->f_mntfromname, sizeof(sp->f_mntfromname)); + strlcpy(sp->f_mntfromname, "[restricted]", + sizeof(sp->f_mntfromname)); + return; + } + /* + * If jail's chroot directory is set to "/" we should be able to see + * all mount-points from inside a jail. + */ + if (strcmp(pr->pr_path, "/") == 0) + return; + len = strlen(pr->pr_path); + strlcpy(jpath, sp->f_mntfromname + len, sizeof(jpath)); + /* + * Clear current buffer data, so we are sure nothing from + * the valid path left there. + */ + bzero(sp->f_mntfromname, sizeof(sp->f_mntfromname)); + if (*jpath == '\0') { + /* Should never happen. */ + *sp->f_mntfromname = '/'; + } else { + strlcpy(sp->f_mntfromname, jpath, sizeof(sp->f_mntfromname)); + } +} + +static void +nullfs_set_mntfromname(struct statfs *sp, char *name) +{ + if (strcmp(sp->f_mntfromname,name) != 0) { + bzero(sp->f_mntfromname, sizeof sp->f_mntfromname); + strlcpy(sp->f_mntfromname, name, sizeof(sp->f_mntfromname)); + } +} + +static int nullfs_statfs(mp, sbp) struct mount *mp; struct statfs *sbp; { int error; struct statfs mstat; + struct thread *td = curthread; + char *fspath, *fbuf; NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, @@ -281,6 +386,36 @@ if (error) return (error); + /* Resolve global filesystem path from lowerrootvp */ + if (vn_fullpath_global(td, + NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp), + &fspath, &fbuf) == 0) { + if (strlen(fspath) < MNAMELEN) { + if (priv_check(td, PRIV_VFS_GENERATION)) { + /* we are jailed, update f_mntfromname */ + bzero(mstat.f_mntfromname, + sizeof(mstat.f_mntfromname)); + strlcpy(mstat.f_mntfromname,fspath, + sizeof(mstat.f_mntfromname)); + mstat.f_fsid.val[0] = mstat.f_fsid.val[1] = 0; + nullfs_prison_enforce_statfs(td->td_ucred, + &mstat); + nullfs_set_mntfromname(sbp, + mstat.f_mntfromname); + } else { + /* we are not jailed */ + nullfs_set_mntfromname(sbp, fspath); + } + } else { + /* we shouldn't be here */ + nullfs_set_mntfromname(sbp, "[invalid]"); + } + free(fbuf, M_TEMP); + } else { + /* source directory (lowerrootvp) doesn't exist */ + nullfs_set_mntfromname(sbp, "[unknown]"); + } + /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; @@ -363,4 +498,4 @@ .vfs_vget = nullfs_vget, }; -VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK); +VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL);