summaryrefslogtreecommitdiffstats
path: root/fs/attr.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-10-08 17:06:08 +0200
committerAl Viro <viro@zeniv.linux.org.uk>2016-10-08 17:06:08 +0200
commite55f1d1d13e7f1c364672d667d78fd1f640ab9f9 (patch)
tree406a3c127abb008f2f736c8d2ee03fc3c2926d6b /fs/attr.c
parentMerge remote-tracking branch 'ovl/misc' into work.misc (diff)
parentfs: Avoid premature clearing of capabilities (diff)
downloadlinux-e55f1d1d13e7f1c364672d667d78fd1f640ab9f9.tar.xz
linux-e55f1d1d13e7f1c364672d667d78fd1f640ab9f9.zip
Merge remote-tracking branch 'jk/vfs' into work.misc
Diffstat (limited to 'fs/attr.c')
-rw-r--r--fs/attr.c35
1 files changed, 23 insertions, 12 deletions
diff --git a/fs/attr.c b/fs/attr.c
index 3c42cab06b5d..a19a64d41e7e 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -17,19 +17,22 @@
#include <linux/ima.h>
/**
- * inode_change_ok - check if attribute changes to an inode are allowed
- * @inode: inode to check
+ * setattr_prepare - check if attribute changes to a dentry are allowed
+ * @dentry: dentry to check
* @attr: attributes to change
*
* Check if we are allowed to change the attributes contained in @attr
- * in the given inode. This includes the normal unix access permission
- * checks, as well as checks for rlimits and others.
+ * in the given dentry. This includes the normal unix access permission
+ * checks, as well as checks for rlimits and others. The function also clears
+ * SGID bit from mode if user is not allowed to set it. Also file capabilities
+ * and IMA extended attributes are cleared if ATTR_KILL_PRIV is set.
*
* Should be called as the first thing in ->setattr implementations,
* possibly after taking additional locks.
*/
-int inode_change_ok(const struct inode *inode, struct iattr *attr)
+int setattr_prepare(struct dentry *dentry, struct iattr *attr)
{
+ struct inode *inode = d_inode(dentry);
unsigned int ia_valid = attr->ia_valid;
/*
@@ -44,7 +47,7 @@ int inode_change_ok(const struct inode *inode, 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,9 +80,19 @@ int inode_change_ok(const struct inode *inode, 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(inode_change_ok);
+EXPORT_SYMBOL(setattr_prepare);
/**
* inode_newsize_ok - may this inode be truncated to a given size
@@ -232,13 +245,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
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;
}
/*