fusefs: fix a permission handling bug during VOP_RENAME

If the file to be renamed is a directory and it's going to get a new parent,
then the user must have write permissions to that directory, because the
".." dirent must be changed.

Reported by:	pjdfstest
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-05-08 22:28:13 +00:00
parent d943c93e76
commit 8e45ec4e64
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=347372
2 changed files with 55 additions and 1 deletions

View File

@ -1423,7 +1423,8 @@ fuse_vnop_rename(struct vop_rename_args *ap)
struct vnode *tvp = ap->a_tvp;
struct componentname *tcnp = ap->a_tcnp;
struct fuse_data *data;
bool newparent = fdvp != tdvp;
bool isdir = fvp->v_type == VDIR;
int err = 0;
if (fuse_isdeadfs(fdvp)) {
@ -1442,7 +1443,17 @@ fuse_vnop_rename(struct vop_rename_args *ap)
* under the source directory in the file system tree.
* Linux performs this check at VFS level.
*/
/*
* If source is a directory, and it will get a new parent, user must
* have write permission to it, so ".." can be modified.
*/
data = fuse_get_mpdata(vnode_mount(tdvp));
if (data->dataflags & FSESS_DEFAULT_PERMISSIONS && isdir && newparent) {
err = fuse_internal_access(fvp, VWRITE,
tcnp->cn_thread, tcnp->cn_cred);
if (err)
goto out;
}
sx_xlock(&data->rename_lock);
err = fuse_internal_rename(fdvp, fcnp, tdvp, tcnp);
if (err == 0) {

View File

@ -833,6 +833,49 @@ TEST_F(Rename, eperm_on_sticky_srcdir)
ASSERT_EQ(EPERM, errno);
}
/*
* A user cannot move out a subdirectory that he does not own, because that
* would require changing the subdirectory's ".." dirent
*/
TEST_F(Rename, eperm_for_subdirectory)
{
const char FULLDST[] = "mountpoint/d/dst";
const char FULLSRC[] = "mountpoint/src";
const char RELDSTDIR[] = "d";
const char RELDST[] = "dst";
const char RELSRC[] = "src";
uint64_t ino = 42;
uint64_t dstdir_ino = 43;
expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0);
expect_lookup(RELSRC, ino, S_IFDIR | 0755, UINT64_MAX, 0);
expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0777, UINT64_MAX, 0);
EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
ASSERT_EQ(EACCES, errno);
}
/*
* A user _can_ rename a subdirectory to which he lacks write permissions, if
* it will keep the same parent
*/
TEST_F(Rename, subdirectory_to_same_dir)
{
const char FULLDST[] = "mountpoint/dst";
const char FULLSRC[] = "mountpoint/src";
const char RELDST[] = "dst";
const char RELSRC[] = "src";
uint64_t ino = 42;
expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0);
expect_lookup(RELSRC, ino, S_IFDIR | 0755, UINT64_MAX, 0);
EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
expect_rename(0);
ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
}
TEST_F(Rename, eperm_on_sticky_dstdir)
{
const char FULLDST[] = "mountpoint/d/dst";