[Trusty][Xenial][PATCH 1/1] v3: CVE-2015-1350 fs: Avoid premature clearing of capabilities

Khalid Elmously khalid.elmously at canonical.com
Wed Dec 13 02:15:15 UTC 2017


From: Jan Kara <jack at suse.cz>


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.


(backported from commit 030b533c4fd4d2ec3402363323de4bb2983c9cee)
[kmously: This is a re-implementation of the upstream commit that doesn't include all the refactoring that was done in upstream)

CVE-2015-1350


References: CVE-2015-1350
Reviewed-by: Christoph Hellwig <hch at lst.de>
Signed-off-by: Jan Kara <jack at suse.cz>
Signed-off-by: Khalid Elmously <khalid.elmously at canonical.com>



---
 fs/attr.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index df05bc167360..870d45103f29 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -68,7 +68,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) && !chown_ok(inode, attr->ia_uid))
@@ -95,6 +95,20 @@ 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;
+		struct dentry *dentry;
+
+		dentry = d_obtain_alias(inode);
+		if (!IS_ERR(dentry)) {
+			error = security_inode_killpriv(dentry);
+			dput(dentry);
+			return error;
+		}
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL(inode_change_ok);
@@ -250,13 +264,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;
 	}
 
 	/*
-- 
2.14.1





More information about the kernel-team mailing list