summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2016-05-26 17:21:32 +0200
committerBen Hutchings <ben@decadent.org.uk>2016-11-20 01:01:44 +0000
commit7230a82ecc91aaf0c62b048afb15f3b8e2d8059f (patch)
tree0f03a9a239dccb8909c732511298223942d8d974
parent44b25c3e25af81daebf188ba1bc94b123ea40138 (diff)
downloadlwn-7230a82ecc91aaf0c62b048afb15f3b8e2d8059f.tar.gz
lwn-7230a82ecc91aaf0c62b048afb15f3b8e2d8059f.zip
fs: Avoid premature clearing of capabilities
commit 030b533c4fd4d2ec3402363323de4bb2983c9cee upstream. Currently, notify_change() clears capabilities or IMA attributes by calling security_inode_killpriv() before calling into ->setattr. Thus it happens before any other permission checks in inode_change_ok() and user is thus allowed to trigger clearing of capabilities or IMA attributes for any file he can look up e.g. by calling chown for that file. This is unexpected and can lead to user DoSing a system. Fix the problem by calling security_inode_killpriv() at the end of inode_change_ok() instead of from notify_change(). At that moment we are sure user has permissions to do the requested change. References: CVE-2015-1350 Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-rw-r--r--fs/attr.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/fs/attr.c b/fs/attr.c
index 0006fdeb721f..a7f0c75734c2 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -46,7 +46,7 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr)
/* If force is set do it anyway. */
if (ia_valid & ATTR_FORCE)
- return 0;
+ goto kill_priv;
/* Make sure a caller can chown. */
if ((ia_valid & ATTR_UID) &&
@@ -77,6 +77,16 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr)
return -EPERM;
}
+kill_priv:
+ /* User has permission for the change */
+ if (ia_valid & ATTR_KILL_PRIV) {
+ int error;
+
+ error = security_inode_killpriv(dentry);
+ if (error)
+ return error;
+ }
+
return 0;
}
EXPORT_SYMBOL(setattr_prepare);
@@ -199,13 +209,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
if (!(ia_valid & ATTR_MTIME_SET))
attr->ia_mtime = now;
if (ia_valid & ATTR_KILL_PRIV) {
- attr->ia_valid &= ~ATTR_KILL_PRIV;
- ia_valid &= ~ATTR_KILL_PRIV;
error = security_inode_need_killpriv(dentry);
- if (error > 0)
- error = security_inode_killpriv(dentry);
- if (error)
+ if (error < 0)
return error;
+ if (error == 0)
+ ia_valid = attr->ia_valid &= ~ATTR_KILL_PRIV;
}
/*