All of lore.kernel.org
 help / color / mirror / Atom feed
From: NeilBrown <neilb@suse.com>
To: Bruce Fields <bfields@fieldses.org>
Cc: Nelson Elhage <nelhage@nelhage.com>,
	Christoph Hellwig <hch@lst.de>,
	linux-nfs@vger.kernel.org, James Brown <jbrown@easypost.com>
Subject: Re: NFSv3 may inappropriately return EPERM for fsetxattr
Date: Thu, 16 Aug 2018 10:39:35 +1000	[thread overview]
Message-ID: <87r2iz9mbc.fsf@notabene.neil.brown.name> (raw)
In-Reply-To: <20180814194334.GO7906@fieldses.org>

[-- Attachment #1: Type: text/plain, Size: 9657 bytes --]

On Tue, Aug 14 2018, Bruce Fields wrote:
> Honestly I'm not completely sure I understand the proposal.

Ok, here is a concrete RFC proposal which should make it easier to
understand.
I've tested that this fixes the specific problem in that a user with a
uid that doesn't match the file, but which the server will give
ownership rights to, can now setacl a file.

Thanks,
NeilBrown

From 34f8b23b224e575d5f1fa30834b247e82a854546 Mon Sep 17 00:00:00 2001
From: NeilBrown <neilb@suse.com>
Date: Thu, 16 Aug 2018 10:37:21 +1000
Subject: [PATCH] VFS: introduce MAY_ACT_AS_OWNER

A few places in VFS, particularly set_posix_acl(), use
inode_owner_or_capable() to check if the caller has "owner"
access to the inode.
This assumes that it is valid to test inode->i_uid, which is not
always the case.  Particularly in the case of NFS it is not valid to
us i_uid (or i_mode) for permission tests - the server needs to make
the decision.

As a result if the server is remaping uids
(e.g. all-squash,anon_uid=1000),
then all users should have ownership access, but most users will not
be able to set acls.

This patch moves the ownership test to inode_permission and
i_op->permission.
A new flag for this functions, MAY_ACT_AS_OWNER is introduced.
generic_permission() now handles this correctly and many
i_op->permission functions call this function() and don't need any
changes.  A few are changed to handle MAY_ACT_AS_OWNER exactly
as generic_permission() does, using inode_owner_or_capable().
For these filesystems, no behavioural change should be noticed.

For NFS, nfs_permission is changed to always return 0 (success) if
MAY_ACT_AS_OWNER.  For NFS, and operations which use this flag should
be sent to the server, and the server will succeed or fail as
appropriate.

nfsd has a similar flag - NFSD_MAY_OWNER_OVERRIDE.  This is currently
implemented by a test on inode->i_uid.  It is changed to map this
flag to MAY_ACT_AS_OWNER.

Fixes: 013cdf1088d7 ("nfs: use generic posix ACL infrastructure for v3 Posix ACLs")
Signed-off-by: NeilBrown <neilb@suse.com>
---
 fs/afs/security.c  | 10 ++++++++++
 fs/attr.c          | 12 +++++-------
 fs/coda/dir.c      | 10 ++++++++++
 fs/fcntl.c         |  2 +-
 fs/fuse/dir.c      | 10 ++++++++++
 fs/namei.c         |  9 +++++++++
 fs/nfs/dir.c       |  8 ++++++++
 fs/nfsd/vfs.c      |  9 +++++----
 fs/posix_acl.c     |  2 +-
 fs/xattr.c         |  2 +-
 include/linux/fs.h |  8 ++++++++
 11 files changed, 68 insertions(+), 14 deletions(-)

diff --git a/fs/afs/security.c b/fs/afs/security.c
index 81dfedb7879f..ac2e39de8bff 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -349,6 +349,16 @@ int afs_permission(struct inode *inode, int mask)
 	if (mask & MAY_NOT_BLOCK)
 		return -ECHILD;
 
+	/* Short-circuit for owner */
+	if (mask & MAY_ACT_AS_OWNER) {
+		if (inode_owner_or_capable(inode))
+			return 0;
+		mask &= ~MAY_ACT_AS_OWNER;
+		if (!mask)
+			/* No other permission will suffice */
+			return -EACCES;
+	}
+
 	_enter("{{%x:%u},%lx},%x,",
 	       vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
 
diff --git a/fs/attr.c b/fs/attr.c
index d22e8187477f..c1160bd9416b 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -87,7 +87,7 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr)
 
 	/* Make sure a caller can chmod. */
 	if (ia_valid & ATTR_MODE) {
-		if (!inode_owner_or_capable(inode))
+		if (inode_permission(inode, MAY_ACT_AS_OWNER) < 0)
 			return -EPERM;
 		/* Also check the setgid bit! */
 		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
@@ -98,7 +98,7 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr)
 
 	/* Check for setting the inode time. */
 	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
-		if (!inode_owner_or_capable(inode))
+		if (inode_permission(inode, MAY_ACT_AS_OWNER) < 0)
 			return -EPERM;
 	}
 
@@ -246,11 +246,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
 		if (IS_IMMUTABLE(inode))
 			return -EPERM;
 
-		if (!inode_owner_or_capable(inode)) {
-			error = inode_permission(inode, MAY_WRITE);
-			if (error)
-				return error;
-		}
+		error = inode_permission(inode, MAY_ACT_AS_OWNER | MAY_WRITE);
+		if (error)
+			return error;
 	}
 
 	if ((ia_valid & ATTR_MODE)) {
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index 00876ddadb43..7e31f68d4973 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -80,6 +80,16 @@ int coda_permission(struct inode *inode, int mask)
 	if (mask & MAY_NOT_BLOCK)
 		return -ECHILD;
 
+	/* Short-circuit for owner */
+	if (mask & MAY_ACT_AS_OWNER) {
+		if (inode_owner_or_capable(inode))
+			return 0;
+		mask &= ~MAY_ACT_AS_OWNER;
+		if (!mask)
+			/* No other permission will suffice */
+			return -EACCES;
+	}
+
 	mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
  
 	if (!mask)
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 12273b6ea56d..dbf2531dc7fa 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -46,7 +46,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
 
 	/* O_NOATIME can only be set by the owner or superuser */
 	if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME))
-		if (!inode_owner_or_capable(inode))
+		if (inode_permission(inode, MAY_ACT_AS_OWNER) < 0)
 			return -EPERM;
 
 	/* required for strict SunOS emulation */
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index d80aab0d5982..46deb2652dca 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1117,6 +1117,16 @@ static int fuse_permission(struct inode *inode, int mask)
 	if (!fuse_allow_current_process(fc))
 		return -EACCES;
 
+	/* Short-circuit for owner */
+	if (mask & MAY_ACT_AS_OWNER) {
+		if (inode_owner_or_capable(inode))
+			return 0;
+		mask &= ~MAY_ACT_AS_OWNER;
+		if (!mask)
+			/* No other permission will suffice */
+			return -EACCES;
+	}
+
 	/*
 	 * If attributes are needed, refresh them before proceeding
 	 */
diff --git a/fs/namei.c b/fs/namei.c
index 3cd396277cd3..364bfafa5b01 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -335,6 +335,15 @@ int generic_permission(struct inode *inode, int mask)
 {
 	int ret;
 
+	/* Short-circuit for owner */
+	if (mask & MAY_ACT_AS_OWNER) {
+		if (inode_owner_or_capable(inode))
+			return 0;
+		mask &= ~MAY_ACT_AS_OWNER;
+		if (!mask)
+			/* No other permission will suffice */
+			return -EACCES;
+	}
 	/*
 	 * Do the basic permission checks.
 	 */
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index d7f158c3efc8..e487429318d8 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2516,6 +2516,14 @@ int nfs_permission(struct inode *inode, int mask)
 
 	nfs_inc_stats(inode, NFSIOS_VFSACCESS);
 
+	/* Short-circuit for owner */
+	if (mask & MAY_ACT_AS_OWNER)
+		/*
+		 * Ownership will be tested by server when we
+		 * actually try operation.
+		 */
+		return 0;
+
 	if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
 		goto out;
 	/* Is this sys_access() ? */
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 55a099e47ba2..467b3418af20 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -2038,12 +2038,13 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
 	 * We must trust the client to do permission checking - using "ACCESS"
 	 * with NFSv3.
 	 */
-	if ((acc & NFSD_MAY_OWNER_OVERRIDE) &&
-	    uid_eq(inode->i_uid, current_fsuid()))
-		return 0;
 
 	/* This assumes  NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
-	err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
+	if (acc & NFSD_MAY_OWNER_OVERRIDE)
+		err = inode_permission(inode, ((acc & (MAY_READ|MAY_WRITE|MAY_EXEC))
+					       | MAY_ACT_AS_OWNER));
+	else
+		err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
 
 	/* Allow read access to binaries even when mode 111 */
 	if (err == -EACCES && S_ISREG(inode->i_mode) &&
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 2fd0fde16fe1..a90c7dca892a 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -863,7 +863,7 @@ set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)
 
 	if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
 		return acl ? -EACCES : 0;
-	if (!inode_owner_or_capable(inode))
+	if (inode_permission(inode, MAY_ACT_AS_OWNER) < 0)
 		return -EPERM;
 
 	if (acl) {
diff --git a/fs/xattr.c b/fs/xattr.c
index f9cb1db187b7..9ce0a0994abd 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -127,7 +127,7 @@ xattr_permission(struct inode *inode, const char *name, int mask)
 		if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
 			return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
 		if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) &&
-		    (mask & MAY_WRITE) && !inode_owner_or_capable(inode))
+		    (mask & MAY_WRITE) && inode_permission(inode, MAY_ACT_AS_OWNER) < 0)
 			return -EPERM;
 	}
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1ec33fd0423f..2641fb50bed0 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -92,6 +92,14 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
 #define MAY_CHDIR		0x00000040
 /* called from RCU mode, don't block */
 #define MAY_NOT_BLOCK		0x00000080
+/*
+ * File Owner is always allowed to perform pending
+ * operation.  If current user is an owner, or if
+ * filesystem performs permission check at time-of-operation,
+ * then succeed, else require some other permission
+ * if listed.
+ */
+#define MAY_ACT_AS_OWNER	0x00000100
 
 /*
  * flags in file.f_mode.  Note that FMODE_READ and FMODE_WRITE must correspond
-- 
2.14.0.rc0.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

  parent reply	other threads:[~2018-08-16  3:34 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-18  3:46 NFSv3 may inappropriately return EPERM for fsetxattr Nelson Elhage
2016-03-21 14:43 ` Christoph Hellwig
2016-03-21 15:56   ` Nelson Elhage
2018-08-10  1:29     ` NeilBrown
2018-08-10 17:00       ` Bruce Fields
2018-08-10 17:03         ` Bruce Fields
2018-08-11 22:28           ` NeilBrown
2018-08-12 13:21             ` Bruce Fields
2018-08-12 23:55               ` NeilBrown
2018-08-14  9:03                 ` NeilBrown
2018-08-14 19:43                   ` Bruce Fields
2018-08-14 23:49                     ` NeilBrown
2018-08-16  0:39                     ` NeilBrown [this message]
2018-08-16 17:54                       ` Bruce Fields
2018-08-16 22:50                         ` NeilBrown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87r2iz9mbc.fsf@notabene.neil.brown.name \
    --to=neilb@suse.com \
    --cc=bfields@fieldses.org \
    --cc=hch@lst.de \
    --cc=jbrown@easypost.com \
    --cc=linux-nfs@vger.kernel.org \
    --cc=nelhage@nelhage.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.