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 --]
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).