[linux] 04/09: posix_acl: Clear SGID bit when setting file permissions (CVE-2016-7097)

debian-kernel at lists.debian.org debian-kernel at lists.debian.org
Thu Dec 1 00:23:47 UTC 2016


This is an automated email from the git hooks/post-receive script.

benh pushed a commit to branch jessie-security
in repository linux.

commit 432e92a7b7dd864c1bdb4bb52af4a0e42b3830ff
Author: Ben Hutchings <ben at decadent.org.uk>
Date:   Wed Nov 30 20:38:38 2016 +0000

    posix_acl: Clear SGID bit when setting file permissions (CVE-2016-7097)
---
 debian/changelog                                   |   1 +
 ...ar-sgid-bit-when-setting-file-permissions.patch | 379 +++++++++++++++++++++
 debian/patches/series                              |   1 +
 3 files changed, 381 insertions(+)

diff --git a/debian/changelog b/debian/changelog
index 377634c..5ade7bb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -9,6 +9,7 @@ linux (3.16.36-1+deb8u3) UNRELEASED; urgency=medium
   * [powerpc*] KVM: Book3S HV: Pull out TM state save/restore into separate
     procedures
   * [powerpc*] KVM: Book3S HV: Save/restore TM state in H_CEDE (CVE-2016-5412)
+  * posix_acl: Clear SGID bit when setting file permissions (CVE-2016-7097)
 
  -- Ben Hutchings <ben at decadent.org.uk>  Wed, 30 Nov 2016 04:06:41 +0000
 
diff --git a/debian/patches/bugfix/all/posix_acl-clear-sgid-bit-when-setting-file-permissions.patch b/debian/patches/bugfix/all/posix_acl-clear-sgid-bit-when-setting-file-permissions.patch
new file mode 100644
index 0000000..358d285
--- /dev/null
+++ b/debian/patches/bugfix/all/posix_acl-clear-sgid-bit-when-setting-file-permissions.patch
@@ -0,0 +1,379 @@
+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 3.16:
+ - Drop changes to orangefs
+ - Adjust context
+ - Update ext3 as well]
+Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
+---
+--- a/fs/9p/acl.c
++++ b/fs/9p/acl.c
+@@ -320,32 +320,26 @@ static int v9fs_xattr_set_acl(struct den
+ 	case 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:
+--- a/fs/btrfs/acl.c
++++ b/fs/btrfs/acl.c
+@@ -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;
+--- a/fs/ceph/acl.c
++++ b/fs/ceph/acl.c
+@@ -108,11 +108,9 @@ int ceph_set_acl(struct inode *inode, st
+ 	case 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:
+--- a/fs/ext2/acl.c
++++ b/fs/ext2/acl.c
+@@ -193,15 +193,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/ext3/acl.c
++++ b/fs/ext3/acl.c
+@@ -195,15 +195,11 @@ __ext3_set_acl(handle_t *handle, struct
+ 		case ACL_TYPE_ACCESS:
+ 			name_index = EXT3_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;
+-					ext3_mark_inode_dirty(handle, inode);
+-					if (error == 0)
+-						acl = NULL;
+-				}
++				inode->i_ctime = CURRENT_TIME_SEC;
++				ext3_mark_inode_dirty(handle, inode);
+ 			}
+ 			break;
+ 
+--- a/fs/ext4/acl.c
++++ b/fs/ext4/acl.c
+@@ -201,15 +201,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
+@@ -213,12 +213,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
+@@ -79,17 +79,11 @@ int gfs2_set_acl(struct inode *inode, st
+ 	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
+@@ -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;
+--- a/fs/jffs2/acl.c
++++ b/fs/jffs2/acl.c
+@@ -236,9 +236,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;
+@@ -250,8 +251,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
+@@ -84,13 +84,11 @@ static int __jfs_set_acl(tid_t tid, stru
+ 	case 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:
+--- a/fs/ocfs2/acl.c
++++ b/fs/ocfs2/acl.c
+@@ -241,14 +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)
++			umode_t mode;
++			ret = posix_acl_update_mode(inode, &mode, &acl);
++			if (ret)
+ 				return ret;
+ 			else {
+-				if (ret == 0)
+-					acl = NULL;
+-
+ 				ret = ocfs2_acl_set_mode(inode, di_bh,
+ 							 handle, mode);
+ 				if (ret)
+--- a/fs/posix_acl.c
++++ b/fs/posix_acl.c
+@@ -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.
+  */
+--- a/fs/reiserfs/xattr_acl.c
++++ b/fs/reiserfs/xattr_acl.c
+@@ -246,13 +246,9 @@ __reiserfs_set_acl(struct reiserfs_trans
+ 	case 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:
+--- a/fs/xfs/xfs_acl.c
++++ b/fs/xfs/xfs_acl.c
+@@ -287,16 +287,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
+@@ -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 *);
diff --git a/debian/patches/series b/debian/patches/series
index 68a0b7c..bc9dd72 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -689,6 +689,7 @@ bugfix/all/fs-avoid-premature-clearing-of-capabilities.patch
 bugfix/arm64/arm64-perf-reject-groups-spanning-multiple-hw-pmus.patch
 bugfix/powerpc/kvm-ppc-book3s-hv-pull-out-tm-state-save-restore-into-separate.patch
 bugfix/powerpc/kvm-ppc-book3s-hv-save-restore-tm-state-in-h_cede.patch
+bugfix/all//posix_acl-clear-sgid-bit-when-setting-file-permissions.patch
 
 # Fix ABI changes
 debian/of-fix-abi-changes.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