mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-10-01 01:35:54 -04:00
437 lines
13 KiB
Diff
437 lines
13 KiB
Diff
|
From 073931017b49d9458aa351605b43a7e34598caef Mon Sep 17 00:00:00 2001
|
||
|
From: Jan Kara <jack@suse.cz>
|
||
|
Date: Mon, 19 Sep 2016 17:39:09 +0200
|
||
|
Subject: posix_acl: Clear SGID bit when setting file permissions
|
||
|
|
||
|
When file permissions are modified via chmod(2) and the user is not in
|
||
|
the owning group or capable of CAP_FSETID, the setgid bit is cleared in
|
||
|
inode_change_ok(). Setting a POSIX ACL via setxattr(2) sets the file
|
||
|
permissions as well as the new ACL, but doesn't clear the setgid bit in
|
||
|
a similar way; this allows to bypass the check in chmod(2). Fix that.
|
||
|
|
||
|
References: CVE-2016-7097
|
||
|
Reviewed-by: Christoph Hellwig <hch@lst.de>
|
||
|
Reviewed-by: Jeff Layton <jlayton@redhat.com>
|
||
|
Signed-off-by: Jan Kara <jack@suse.cz>
|
||
|
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
|
||
|
---
|
||
|
fs/9p/acl.c | 40 +++++++++++++++++-----------------------
|
||
|
fs/btrfs/acl.c | 6 ++----
|
||
|
fs/ceph/acl.c | 6 ++----
|
||
|
fs/ext2/acl.c | 12 ++++--------
|
||
|
fs/ext4/acl.c | 12 ++++--------
|
||
|
fs/f2fs/acl.c | 6 ++----
|
||
|
fs/gfs2/acl.c | 12 +++---------
|
||
|
fs/hfsplus/posix_acl.c | 4 ++--
|
||
|
fs/jffs2/acl.c | 9 ++++-----
|
||
|
fs/jfs/acl.c | 6 ++----
|
||
|
fs/ocfs2/acl.c | 10 ++++------
|
||
|
fs/orangefs/acl.c | 15 +++++----------
|
||
|
fs/posix_acl.c | 31 +++++++++++++++++++++++++++++++
|
||
|
fs/reiserfs/xattr_acl.c | 8 ++------
|
||
|
fs/xfs/xfs_acl.c | 13 ++++---------
|
||
|
include/linux/posix_acl.h | 1 +
|
||
|
16 files changed, 89 insertions(+), 102 deletions(-)
|
||
|
|
||
|
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
|
||
|
index 5b6a174..b3c2cc7 100644
|
||
|
--- a/fs/9p/acl.c
|
||
|
+++ b/fs/9p/acl.c
|
||
|
@@ -276,32 +276,26 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
|
||
|
switch (handler->flags) {
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
if (acl) {
|
||
|
- umode_t mode = inode->i_mode;
|
||
|
- retval = posix_acl_equiv_mode(acl, &mode);
|
||
|
- if (retval < 0)
|
||
|
+ struct iattr iattr;
|
||
|
+
|
||
|
+ retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
|
||
|
+ if (retval)
|
||
|
goto err_out;
|
||
|
- else {
|
||
|
- struct iattr iattr;
|
||
|
- if (retval == 0) {
|
||
|
- /*
|
||
|
- * ACL can be represented
|
||
|
- * by the mode bits. So don't
|
||
|
- * update ACL.
|
||
|
- */
|
||
|
- acl = NULL;
|
||
|
- value = NULL;
|
||
|
- size = 0;
|
||
|
- }
|
||
|
- /* Updte the mode bits */
|
||
|
- iattr.ia_mode = ((mode & S_IALLUGO) |
|
||
|
- (inode->i_mode & ~S_IALLUGO));
|
||
|
- iattr.ia_valid = ATTR_MODE;
|
||
|
- /* FIXME should we update ctime ?
|
||
|
- * What is the following setxattr update the
|
||
|
- * mode ?
|
||
|
+ if (!acl) {
|
||
|
+ /*
|
||
|
+ * ACL can be represented
|
||
|
+ * by the mode bits. So don't
|
||
|
+ * update ACL.
|
||
|
*/
|
||
|
- v9fs_vfs_setattr_dotl(dentry, &iattr);
|
||
|
+ value = NULL;
|
||
|
+ size = 0;
|
||
|
}
|
||
|
+ iattr.ia_valid = ATTR_MODE;
|
||
|
+ /* FIXME should we update ctime ?
|
||
|
+ * What is the following setxattr update the
|
||
|
+ * mode ?
|
||
|
+ */
|
||
|
+ v9fs_vfs_setattr_dotl(dentry, &iattr);
|
||
|
}
|
||
|
break;
|
||
|
case ACL_TYPE_DEFAULT:
|
||
|
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
|
||
|
index 53bb7af..247b8df 100644
|
||
|
--- a/fs/btrfs/acl.c
|
||
|
+++ b/fs/btrfs/acl.c
|
||
|
@@ -79,11 +79,9 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- ret = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||
|
- if (ret < 0)
|
||
|
+ ret = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||
|
+ if (ret)
|
||
|
return ret;
|
||
|
- if (ret == 0)
|
||
|
- acl = NULL;
|
||
|
}
|
||
|
ret = 0;
|
||
|
break;
|
||
|
diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
|
||
|
index 4f67227..d0b6b342 100644
|
||
|
--- a/fs/ceph/acl.c
|
||
|
+++ b/fs/ceph/acl.c
|
||
|
@@ -95,11 +95,9 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- ret = posix_acl_equiv_mode(acl, &new_mode);
|
||
|
- if (ret < 0)
|
||
|
+ ret = posix_acl_update_mode(inode, &new_mode, &acl);
|
||
|
+ if (ret)
|
||
|
goto out;
|
||
|
- if (ret == 0)
|
||
|
- acl = NULL;
|
||
|
}
|
||
|
break;
|
||
|
case ACL_TYPE_DEFAULT:
|
||
|
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
|
||
|
index 42f1d18..e725aa0 100644
|
||
|
--- a/fs/ext2/acl.c
|
||
|
+++ b/fs/ext2/acl.c
|
||
|
@@ -190,15 +190,11 @@ ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||
|
- if (error < 0)
|
||
|
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||
|
+ if (error)
|
||
|
return error;
|
||
|
- else {
|
||
|
- inode->i_ctime = CURRENT_TIME_SEC;
|
||
|
- mark_inode_dirty(inode);
|
||
|
- if (error == 0)
|
||
|
- acl = NULL;
|
||
|
- }
|
||
|
+ inode->i_ctime = CURRENT_TIME_SEC;
|
||
|
+ mark_inode_dirty(inode);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
|
||
|
index c6601a4..dfa5199 100644
|
||
|
--- a/fs/ext4/acl.c
|
||
|
+++ b/fs/ext4/acl.c
|
||
|
@@ -193,15 +193,11 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||
|
- if (error < 0)
|
||
|
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||
|
+ if (error)
|
||
|
return error;
|
||
|
- else {
|
||
|
- inode->i_ctime = ext4_current_time(inode);
|
||
|
- ext4_mark_inode_dirty(handle, inode);
|
||
|
- if (error == 0)
|
||
|
- acl = NULL;
|
||
|
- }
|
||
|
+ inode->i_ctime = ext4_current_time(inode);
|
||
|
+ ext4_mark_inode_dirty(handle, inode);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
|
||
|
index 4dcc9e2..3134424 100644
|
||
|
--- a/fs/f2fs/acl.c
|
||
|
+++ b/fs/f2fs/acl.c
|
||
|
@@ -210,12 +210,10 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||
|
- if (error < 0)
|
||
|
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||
|
+ if (error)
|
||
|
return error;
|
||
|
set_acl_inode(inode, inode->i_mode);
|
||
|
- if (error == 0)
|
||
|
- acl = NULL;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
|
||
|
index 363ba9e..2524807 100644
|
||
|
--- a/fs/gfs2/acl.c
|
||
|
+++ b/fs/gfs2/acl.c
|
||
|
@@ -92,17 +92,11 @@ int __gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||
|
if (type == ACL_TYPE_ACCESS) {
|
||
|
umode_t mode = inode->i_mode;
|
||
|
|
||
|
- error = posix_acl_equiv_mode(acl, &mode);
|
||
|
- if (error < 0)
|
||
|
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||
|
+ if (error)
|
||
|
return error;
|
||
|
-
|
||
|
- if (error == 0)
|
||
|
- acl = NULL;
|
||
|
-
|
||
|
- if (mode != inode->i_mode) {
|
||
|
- inode->i_mode = mode;
|
||
|
+ if (mode != inode->i_mode)
|
||
|
mark_inode_dirty(inode);
|
||
|
- }
|
||
|
}
|
||
|
|
||
|
if (acl) {
|
||
|
diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c
|
||
|
index ab7ea25..9b92058 100644
|
||
|
--- a/fs/hfsplus/posix_acl.c
|
||
|
+++ b/fs/hfsplus/posix_acl.c
|
||
|
@@ -65,8 +65,8 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- err = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||
|
- if (err < 0)
|
||
|
+ err = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||
|
+ if (err)
|
||
|
return err;
|
||
|
}
|
||
|
err = 0;
|
||
|
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
|
||
|
index bc2693d..2a0f2a1 100644
|
||
|
--- a/fs/jffs2/acl.c
|
||
|
+++ b/fs/jffs2/acl.c
|
||
|
@@ -233,9 +233,10 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- umode_t mode = inode->i_mode;
|
||
|
- rc = posix_acl_equiv_mode(acl, &mode);
|
||
|
- if (rc < 0)
|
||
|
+ umode_t mode;
|
||
|
+
|
||
|
+ rc = posix_acl_update_mode(inode, &mode, &acl);
|
||
|
+ if (rc)
|
||
|
return rc;
|
||
|
if (inode->i_mode != mode) {
|
||
|
struct iattr attr;
|
||
|
@@ -247,8 +248,6 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||
|
if (rc < 0)
|
||
|
return rc;
|
||
|
}
|
||
|
- if (rc == 0)
|
||
|
- acl = NULL;
|
||
|
}
|
||
|
break;
|
||
|
case ACL_TYPE_DEFAULT:
|
||
|
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
|
||
|
index 21fa92b..3a1e155 100644
|
||
|
--- a/fs/jfs/acl.c
|
||
|
+++ b/fs/jfs/acl.c
|
||
|
@@ -78,13 +78,11 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- rc = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||
|
- if (rc < 0)
|
||
|
+ rc = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||
|
+ if (rc)
|
||
|
return rc;
|
||
|
inode->i_ctime = CURRENT_TIME;
|
||
|
mark_inode_dirty(inode);
|
||
|
- if (rc == 0)
|
||
|
- acl = NULL;
|
||
|
}
|
||
|
break;
|
||
|
case ACL_TYPE_DEFAULT:
|
||
|
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
|
||
|
index 2162434..164307b 100644
|
||
|
--- a/fs/ocfs2/acl.c
|
||
|
+++ b/fs/ocfs2/acl.c
|
||
|
@@ -241,13 +241,11 @@ int ocfs2_set_acl(handle_t *handle,
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- umode_t mode = inode->i_mode;
|
||
|
- ret = posix_acl_equiv_mode(acl, &mode);
|
||
|
- if (ret < 0)
|
||
|
- return ret;
|
||
|
+ umode_t mode;
|
||
|
|
||
|
- if (ret == 0)
|
||
|
- acl = NULL;
|
||
|
+ ret = posix_acl_update_mode(inode, &mode, &acl);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
|
||
|
ret = ocfs2_acl_set_mode(inode, di_bh,
|
||
|
handle, mode);
|
||
|
diff --git a/fs/orangefs/acl.c b/fs/orangefs/acl.c
|
||
|
index 28f2195..7a37544 100644
|
||
|
--- a/fs/orangefs/acl.c
|
||
|
+++ b/fs/orangefs/acl.c
|
||
|
@@ -73,14 +73,11 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- umode_t mode = inode->i_mode;
|
||
|
- /*
|
||
|
- * can we represent this with the traditional file
|
||
|
- * mode permission bits?
|
||
|
- */
|
||
|
- error = posix_acl_equiv_mode(acl, &mode);
|
||
|
- if (error < 0) {
|
||
|
- gossip_err("%s: posix_acl_equiv_mode err: %d\n",
|
||
|
+ umode_t mode;
|
||
|
+
|
||
|
+ error = posix_acl_update_mode(inode, &mode, &acl);
|
||
|
+ if (error) {
|
||
|
+ gossip_err("%s: posix_acl_update_mode err: %d\n",
|
||
|
__func__,
|
||
|
error);
|
||
|
return error;
|
||
|
@@ -90,8 +87,6 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||
|
SetModeFlag(orangefs_inode);
|
||
|
inode->i_mode = mode;
|
||
|
mark_inode_dirty_sync(inode);
|
||
|
- if (error == 0)
|
||
|
- acl = NULL;
|
||
|
}
|
||
|
break;
|
||
|
case ACL_TYPE_DEFAULT:
|
||
|
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
|
||
|
index 59d47ab0..bfc3ec3 100644
|
||
|
--- a/fs/posix_acl.c
|
||
|
+++ b/fs/posix_acl.c
|
||
|
@@ -626,6 +626,37 @@ no_mem:
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(posix_acl_create);
|
||
|
|
||
|
+/**
|
||
|
+ * posix_acl_update_mode - update mode in set_acl
|
||
|
+ *
|
||
|
+ * Update the file mode when setting an ACL: compute the new file permission
|
||
|
+ * bits based on the ACL. In addition, if the ACL is equivalent to the new
|
||
|
+ * file mode, set *acl to NULL to indicate that no ACL should be set.
|
||
|
+ *
|
||
|
+ * As with chmod, clear the setgit bit if the caller is not in the owning group
|
||
|
+ * or capable of CAP_FSETID (see inode_change_ok).
|
||
|
+ *
|
||
|
+ * Called from set_acl inode operations.
|
||
|
+ */
|
||
|
+int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
|
||
|
+ struct posix_acl **acl)
|
||
|
+{
|
||
|
+ umode_t mode = inode->i_mode;
|
||
|
+ int error;
|
||
|
+
|
||
|
+ error = posix_acl_equiv_mode(*acl, &mode);
|
||
|
+ if (error < 0)
|
||
|
+ return error;
|
||
|
+ if (error == 0)
|
||
|
+ *acl = NULL;
|
||
|
+ if (!in_group_p(inode->i_gid) &&
|
||
|
+ !capable_wrt_inode_uidgid(inode, CAP_FSETID))
|
||
|
+ mode &= ~S_ISGID;
|
||
|
+ *mode_p = mode;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(posix_acl_update_mode);
|
||
|
+
|
||
|
/*
|
||
|
* Fix up the uids and gids in posix acl extended attributes in place.
|
||
|
*/
|
||
|
diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
|
||
|
index dbed42f..2737668 100644
|
||
|
--- a/fs/reiserfs/xattr_acl.c
|
||
|
+++ b/fs/reiserfs/xattr_acl.c
|
||
|
@@ -242,13 +242,9 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
|
||
|
case ACL_TYPE_ACCESS:
|
||
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||
|
if (acl) {
|
||
|
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||
|
- if (error < 0)
|
||
|
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||
|
+ if (error)
|
||
|
return error;
|
||
|
- else {
|
||
|
- if (error == 0)
|
||
|
- acl = NULL;
|
||
|
- }
|
||
|
}
|
||
|
break;
|
||
|
case ACL_TYPE_DEFAULT:
|
||
|
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
|
||
|
index b6e527b..8a0dec8 100644
|
||
|
--- a/fs/xfs/xfs_acl.c
|
||
|
+++ b/fs/xfs/xfs_acl.c
|
||
|
@@ -257,16 +257,11 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||
|
return error;
|
||
|
|
||
|
if (type == ACL_TYPE_ACCESS) {
|
||
|
- umode_t mode = inode->i_mode;
|
||
|
- error = posix_acl_equiv_mode(acl, &mode);
|
||
|
-
|
||
|
- if (error <= 0) {
|
||
|
- acl = NULL;
|
||
|
-
|
||
|
- if (error < 0)
|
||
|
- return error;
|
||
|
- }
|
||
|
+ umode_t mode;
|
||
|
|
||
|
+ error = posix_acl_update_mode(inode, &mode, &acl);
|
||
|
+ if (error)
|
||
|
+ return error;
|
||
|
error = xfs_set_mode(inode, mode);
|
||
|
if (error)
|
||
|
return error;
|
||
|
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
|
||
|
index d5d3d74..bf1046d 100644
|
||
|
--- a/include/linux/posix_acl.h
|
||
|
+++ b/include/linux/posix_acl.h
|
||
|
@@ -93,6 +93,7 @@ extern int set_posix_acl(struct inode *, int, struct posix_acl *);
|
||
|
extern int posix_acl_chmod(struct inode *, umode_t);
|
||
|
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
|
||
|
struct posix_acl **);
|
||
|
+extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **);
|
||
|
|
||
|
extern int simple_set_acl(struct inode *, struct posix_acl *, int);
|
||
|
extern int simple_acl_create(struct inode *, struct inode *);
|
||
|
--
|
||
|
cgit v1.1
|
||
|
|