[linux] 02/03: posix_acl: Clear SGID bit when setting file permissions (CVE-2016-7097)
debian-kernel at lists.debian.org
debian-kernel at lists.debian.org
Wed Oct 12 23:28:14 UTC 2016
This is an automated email from the git hooks/post-receive script.
benh pushed a commit to branch sid
in repository linux.
commit efccbd4eb25c9f0426d3e7349af6464ab26861a1
Author: Ben Hutchings <ben at decadent.org.uk>
Date: Thu Oct 13 00:23:11 2016 +0100
posix_acl: Clear SGID bit when setting file permissions (CVE-2016-7097)
---
debian/changelog | 1 +
...lear-sgid-bit-when-setting-file-permissio.patch | 402 +++++++++++++++++++++
debian/patches/series | 1 +
3 files changed, 404 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index 81d4c64..61d6a60 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -126,6 +126,7 @@ linux (4.7.7-1) UNRELEASED; urgency=medium
[ Ben Hutchings ]
* net: add recursion limit to GRO (CVE-2016-7039)
+ * posix_acl: Clear SGID bit when setting file permissions (CVE-2016-7097)
-- Ben Hutchings <ben at decadent.org.uk> Tue, 11 Oct 2016 22:43:14 +0100
diff --git a/debian/patches/bugfix/all/posix_acl-clear-sgid-bit-when-setting-file-permissio.patch b/debian/patches/bugfix/all/posix_acl-clear-sgid-bit-when-setting-file-permissio.patch
new file mode 100644
index 0000000..592625e
--- /dev/null
+++ b/debian/patches/bugfix/all/posix_acl-clear-sgid-bit-when-setting-file-permissio.patch
@@ -0,0 +1,402 @@
+From: Jan Kara <jack at suse.cz>
+Date: Mon, 19 Sep 2016 17:39:09 +0200
+Subject: posix_acl: Clear SGID bit when setting file permissions
+Origin: https://git.kernel.org/linus/073931017b49d9458aa351605b43a7e34598caef
+
+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 at lst.de>
+Reviewed-by: Jeff Layton <jlayton at redhat.com>
+Signed-off-by: Jan Kara <jack at suse.cz>
+Signed-off-by: Andreas Gruenbacher <agruenba at redhat.com>
+[bwh: Backported to 4.7: adjust context]
+---
+ 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(-)
+
+--- a/fs/9p/acl.c
++++ b/fs/9p/acl.c
+@@ -276,32 +276,26 @@ static int v9fs_xattr_set_acl(const stru
+ 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:
+--- a/fs/btrfs/acl.c
++++ b/fs/btrfs/acl.c
+@@ -80,11 +80,9 @@ static int __btrfs_set_acl(struct btrfs_
+ 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;
+--- a/fs/ceph/acl.c
++++ b/fs/ceph/acl.c
+@@ -95,11 +95,9 @@ int ceph_set_acl(struct inode *inode, st
+ 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:
+--- a/fs/ext2/acl.c
++++ b/fs/ext2/acl.c
+@@ -190,15 +190,11 @@ ext2_set_acl(struct inode *inode, struct
+ 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;
+
+--- a/fs/ext4/acl.c
++++ b/fs/ext4/acl.c
+@@ -193,15 +193,11 @@ __ext4_set_acl(handle_t *handle, struct
+ 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;
+
+--- a/fs/f2fs/acl.c
++++ b/fs/f2fs/acl.c
+@@ -211,12 +211,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;
+
+--- a/fs/gfs2/acl.c
++++ b/fs/gfs2/acl.c
+@@ -92,17 +92,11 @@ int __gfs2_set_acl(struct inode *inode,
+ 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) {
+--- a/fs/hfsplus/posix_acl.c
++++ b/fs/hfsplus/posix_acl.c
+@@ -65,8 +65,8 @@ int hfsplus_set_posix_acl(struct inode *
+ 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;
+--- a/fs/jffs2/acl.c
++++ b/fs/jffs2/acl.c
+@@ -233,9 +233,10 @@ int jffs2_set_acl(struct inode *inode, s
+ 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, s
+ if (rc < 0)
+ return rc;
+ }
+- if (rc == 0)
+- acl = NULL;
+ }
+ break;
+ case ACL_TYPE_DEFAULT:
+--- a/fs/jfs/acl.c
++++ b/fs/jfs/acl.c
+@@ -78,13 +78,11 @@ static int __jfs_set_acl(tid_t tid, stru
+ 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:
+--- 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);
+--- a/fs/orangefs/acl.c
++++ b/fs/orangefs/acl.c
+@@ -76,14 +76,11 @@ int orangefs_set_acl(struct inode *inode
+ case ACL_TYPE_ACCESS:
+ name = ORANGEFS_XATTR_NAME_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;
+@@ -93,8 +90,6 @@ int orangefs_set_acl(struct inode *inode
+ SetModeFlag(orangefs_inode);
+ inode->i_mode = mode;
+ mark_inode_dirty_sync(inode);
+- if (error == 0)
+- acl = NULL;
+ }
+ break;
+ case ACL_TYPE_DEFAULT:
+--- 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.
+ */
+--- a/fs/reiserfs/xattr_acl.c
++++ b/fs/reiserfs/xattr_acl.c
+@@ -242,13 +242,9 @@ __reiserfs_set_acl(struct reiserfs_trans
+ 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:
+--- a/fs/xfs/xfs_acl.c
++++ b/fs/xfs/xfs_acl.c
+@@ -257,16 +257,11 @@ xfs_set_acl(struct inode *inode, struct
+ 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;
+--- a/include/linux/posix_acl.h
++++ b/include/linux/posix_acl.h
+@@ -93,6 +93,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 *);
diff --git a/debian/patches/series b/debian/patches/series
index 950f06e..8b43b1c 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -115,6 +115,7 @@ features/all/securelevel/arm64-add-kernel-config-option-to-set-securelevel-wh.pa
bugfix/all/ptrace-being-capable-wrt-a-process-requires-mapped-uids-gids.patch
debian/i386-686-pae-pci-set-pci-nobios-by-default.patch
bugfix/all/net-add-recursion-limit-to-gro.patch
+bugfix/all/posix_acl-clear-sgid-bit-when-setting-file-permissio.patch
# ABI maintenance
debian/i8042-revert-abi-break-in-4.7.3.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git
More information about the Kernel-svn-changes
mailing list