Patch "posix_acl: Clear SGID bit when setting file permissions" has been added to the 3.18-stable tree
Subsystems (14)
This is a note to let you know that I've just added the patch titled
posix_acl: Clear SGID bit when setting file permissions
to the 3.18-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
posix_acl-clear-sgid-bit-when-setting-file-permissions.patch
and it can be found in the queue-3.18 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
073931017b49d9458aa351605b43a7e34598caefMon Sep 17 00:00:00 2001
From: Jan Kara <[email protected]>
Date: Mon, 19 Sep 2016 17:39:09 +0200
Subject: posix_acl: Clear SGID bit when setting file permissions
From: Jan Kara <[email protected]>
commit 073931017b49d9458aa351605b43a7e34598caef upstream.
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
--- 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 | 20 ++++++++------------ fs/posix_acl.c | 31 +++++++++++++++++++++++++++++++ fs/reiserfs/xattr_acl.c | 8 ++------ fs/xfs/xfs_acl.c | 13 ++++--------- include/linux/posix_acl.h | 1 + 15 files changed, 88 insertions(+), 98 deletions(-)
fs/9p/acl.c +17 -23
@@ -320,32 +320,26 @@ static int v9fs_xattr_set_acl(struct dencase ACL_TYPE_ACCESS : name =POSIX_ACL_XATTR_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 :
fs/btrfs/acl.c +2 -4
@@ -83,11 +83,9 @@ static int __btrfs_set_acl(struct btrfs_case ACL_TYPE_ACCESS : name =POSIX_ACL_XATTR_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 ;
fs/ceph/acl.c +2 -4
@@ -108,11 +108,9 @@ int ceph_set_acl(struct inode *inode, stcase ACL_TYPE_ACCESS : name =POSIX_ACL_XATTR_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 :
fs/ext2/acl.c +4 -8
@@ -193,15 +193,11 @@ ext2_set_acl(struct inode *inode, structcase 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 ;
fs/ext4/acl.c +4 -8
@@ -201,15 +201,11 @@ __ext4_set_acl(handle_t *handle, structcase 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 ;
fs/f2fs/acl.c +2 -4
@@ -207,12 +207,10 @@ static int __f2fs_set_acl(struct inode *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 (fi, inode->i_mode); -if (error ==0 ) - acl =NULL ; }break ;
fs/gfs2/acl.c +3 -9
@@ -79,17 +79,11 @@ int gfs2_set_acl(struct inode *inode, stif (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) {
fs/hfsplus/posix_acl.c +2 -2
@@ -68,8 +68,8 @@ int hfsplus_set_posix_acl(struct inode *case ACL_TYPE_ACCESS : xattr_name =POSIX_ACL_XATTR_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 ;
fs/jffs2/acl.c +4 -5
@@ -235,9 +235,10 @@ int jffs2_set_acl(struct inode *inode, scase 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;
@@ -249,8 +250,6 @@ int jffs2_set_acl(struct inode *inode, sif (rc <0 )return rc; } -if (rc ==0 ) - acl =NULL ; }break ;case ACL_TYPE_DEFAULT :
fs/jfs/acl.c +2 -4
@@ -84,13 +84,11 @@ static int __jfs_set_acl(tid_t tid, strucase ACL_TYPE_ACCESS : ea_name =POSIX_ACL_XATTR_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 :
fs/ocfs2/acl.c +8 -12
@@ -241,20 +241,16 @@ 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; -else { -if (ret ==0 ) - acl =NULL ; +umode_t mode; - ret =ocfs2_acl_set_mode (inode, di_bh, - handle, mode); -if (ret) -return ret; + ret =posix_acl_update_mode (inode, &mode, &acl); +if (ret) +return ret; - } + ret =ocfs2_acl_set_mode (inode, di_bh, + handle, mode); +if (ret) +return ret; }break ;case ACL_TYPE_DEFAULT :
fs/posix_acl.c +31 -0
@@ -594,6 +594,37 @@ no_acl: }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. */
fs/reiserfs/xattr_acl.c +2 -6
@@ -246,13 +246,9 @@ __reiserfs_set_acl(struct reiserfs_transcase ACL_TYPE_ACCESS : name =POSIX_ACL_XATTR_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 :
fs/xfs/xfs_acl.c +4 -9
@@ -286,16 +286,11 @@ xfs_set_acl(struct inode *inode, structreturn 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;
include/linux/posix_acl.h +1 -0
@@ -95,6 +95,7 @@ extern int set_posix_acl(struct inode *,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 *);
Patches currently in stable-queue which might be from [email protected] are
queue-3.18/posix_acl-clear-sgid-bit-when-setting-file-permissions.patch