Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 7 May 2019 01:27:24 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r347217 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs
Message-ID:  <201905070127.x471ROvf000119@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: asomers
Date: Tue May  7 01:27:23 2019
New Revision: 347217
URL: https://svnweb.freebsd.org/changeset/base/347217

Log:
  fusefs: allow the null chown and null chgrp
  
  Even an unprivileged user should be able to chown a file to its current
  owner, or chgrp it to its current group.  Those are no-ops.
  
  Reported by:	pjdfstest
  Sponsored by:	The FreeBSD Foundation

Modified:
  projects/fuse2/sys/fs/fuse/fuse_vnops.c
  projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc

Modified: projects/fuse2/sys/fs/fuse/fuse_vnops.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_vnops.c	Tue May  7 01:18:57 2019	(r347216)
+++ projects/fuse2/sys/fs/fuse/fuse_vnops.c	Tue May  7 01:27:23 2019	(r347217)
@@ -1519,15 +1519,21 @@ fuse_vnop_setattr(struct vop_setattr_args *ap)
 	struct thread *td = curthread;
 	struct mount *mp;
 	struct fuse_data *data;
+	struct vattr old_va;
 	int dataflags;
-	int err = 0;
+	int err = 0, err2;
 	accmode_t accmode = 0;
 	bool checkperm;
+	gid_t cr_gid;
 
 	mp = vnode_mount(vp);
 	data = fuse_get_mpdata(mp);
 	dataflags = data->dataflags;
 	checkperm = dataflags & FSESS_DEFAULT_PERMISSIONS;
+	if (cred->cr_ngroups > 0)
+		cr_gid = cred->cr_groups[0];
+	else
+		cr_gid = 0;
 
 	if (fuse_isdeadfs(vp)) {
 		return ENXIO;
@@ -1537,10 +1543,20 @@ fuse_vnop_setattr(struct vop_setattr_args *ap)
 		if (checkperm) {
 			/* Only root may change a file's owner */
 			err = priv_check_cred(cred, PRIV_VFS_CHOWN);
-			if (err)
-				return err;;
-		}
-		accmode |= VADMIN;
+			if (err) {
+				/* As a special case, allow the null chown */
+				err2 = fuse_internal_getattr(vp, &old_va, cred,
+					td);
+				if (err2)
+					return (err2);
+				if (vap->va_uid != old_va.va_uid)
+					return err;
+				else
+					accmode |= VADMIN;
+			} else
+				accmode |= VADMIN;
+		} else
+			accmode |= VADMIN;
 	}
 	if (vap->va_gid != (gid_t)VNOVAL) {
 		if (checkperm && !groupmember(vap->va_gid, cred))
@@ -1550,10 +1566,20 @@ fuse_vnop_setattr(struct vop_setattr_args *ap)
 			 * groups 
 			 */
 			err = priv_check_cred(cred, PRIV_VFS_CHOWN);
-			if (err)
-				return err;
-		}
-		accmode |= VADMIN;
+			if (err) {
+				/* As a special case, allow the null chgrp */
+				err2 = fuse_internal_getattr(vp, &old_va, cred,
+					td);
+				if (err2)
+					return (err2);
+				if (vap->va_gid != old_va.va_gid)
+					return err;
+				else
+					accmode |= VADMIN;
+			} else
+				accmode |= VADMIN;
+		} else
+			accmode |= VADMIN;
 	}
 	if (vap->va_size != VNOVAL) {
 		switch (vp->v_type) {
@@ -1591,7 +1617,6 @@ fuse_vnop_setattr(struct vop_setattr_args *ap)
 		    && priv_check_cred(cred, PRIV_VFS_STICKYFILE))
 			return EFTYPE;
 		if (checkperm && (vap->va_mode & S_ISGID)) {
-			struct vattr old_va;
 			err = fuse_internal_getattr(vp, &old_va, cred, td);
 			if (err)
 				return (err);

Modified: projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc	Tue May  7 01:18:57 2019	(r347216)
+++ projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc	Tue May  7 01:27:23 2019	(r347217)
@@ -294,6 +294,34 @@ TEST_F(Access, ok)
 	ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
 }
 
+/* Unprivileged users may chown a file to their own uid */
+TEST_F(Chown, chown_to_self)
+{
+	const char FULLPATH[] = "mountpoint/some_file.txt";
+	const char RELPATH[] = "some_file.txt";
+	const uint64_t ino = 42;
+	const mode_t mode = 0755;
+	uid_t uid;
+
+	uid = geteuid();
+
+	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1, uid);
+	expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, uid);
+	/* The OS may optimize chown by omitting the redundant setattr */
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([](auto in) {
+			return (in->header.opcode == FUSE_SETATTR);
+		}, Eq(true)),
+		_)
+	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out){
+		SET_OUT_HEADER_LEN(out, attr);
+		out->body.attr.attr.mode = S_IFREG | mode;
+		out->body.attr.attr.uid = uid;
+	})));
+
+	EXPECT_EQ(0, chown(FULLPATH, uid, -1)) << strerror(errno);
+}
+
 /* Only root may change a file's owner */
 TEST_F(Chown, eperm)
 {
@@ -357,19 +385,14 @@ TEST_F(Chgrp, ok)
 
 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1, uid, gid);
 	expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, uid, gid);
+	/* The OS may optimize chgrp by omitting the redundant setattr */
 	EXPECT_CALL(*m_mock, process(
 		ResultOf([](auto in) {
-			return (in->header.opcode == FUSE_SETATTR);
-		}, Eq(true)),
-		_)
-	).Times(0);
-	EXPECT_CALL(*m_mock, process(
-		ResultOf([](auto in) {
 			return (in->header.opcode == FUSE_SETATTR &&
 				in->header.nodeid == ino);
 		}, Eq(true)),
 		_)
-	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
+	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out){
 		SET_OUT_HEADER_LEN(out, attr);
 		out->body.attr.attr.mode = S_IFREG | mode;
 		out->body.attr.attr.uid = uid;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201905070127.x471ROvf000119>