From ff0bff80783d049ad584e9a0497350211770f138 Mon Sep 17 00:00:00 2001 From: Sona Sarmadi Date: Fri, 3 Feb 2017 11:01:29 +0100 Subject: kernel: CVE-2016-7097 Setting a POSIX ACL via setxattr doesn't clear the setgid bit The filesystem implementation in the Linux kernel through 4.8.2 preserves the setgid bit during a setxattr call, which allows local users to gain group privileges by leveraging the existence of a setgid program with restrictions on execute permissions. Reference: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-7097 Reference to upstream commits (kernel.org 3.12 branch): https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/log/?id=refs%2Ftags%2Fv3.12.70&qt=grep&q=posix_acl%3A+Clear+SGID+bit+when+setting+file+permissions Signed-off-by: Sona Sarmadi Signed-off-by: Adrian Dudau --- .../linux/files/0002-tmpfs-CVE-2016-7097.patch | 64 +++ .../linux/files/tmpfs-CVE-2016-7097.patch | 440 +++++++++++++++++++++ recipes-kernel/linux/linux-qoriq_3.12.bbappend | 1 + 3 files changed, 505 insertions(+) create mode 100644 recipes-kernel/linux/files/0002-tmpfs-CVE-2016-7097.patch create mode 100644 recipes-kernel/linux/files/tmpfs-CVE-2016-7097.patch diff --git a/recipes-kernel/linux/files/0002-tmpfs-CVE-2016-7097.patch b/recipes-kernel/linux/files/0002-tmpfs-CVE-2016-7097.patch new file mode 100644 index 0000000..7d8d429 --- /dev/null +++ b/recipes-kernel/linux/files/0002-tmpfs-CVE-2016-7097.patch @@ -0,0 +1,64 @@ +From b0369e53c851f8cd87afd059d360a4f646840c8c Mon Sep 17 00:00:00 2001 +From: Gu Zheng +Date: Mon, 9 Jan 2017 09:34:48 +0800 +Subject: tmpfs: clear S_ISGID when setting posix ACLs + +commit 497de07d89c1410d76a15bec2bb41f24a2a89f31 upstream. + +This change was missed the tmpfs modification in In CVE-2016-7097 +commit 073931017b49 ("posix_acl: Clear SGID bit when setting +file permissions") +It can test by xfstest generic/375, which failed to clear +setgid bit in the following test case on tmpfs: + + touch $testfile + chown 100:100 $testfile + chmod 2755 $testfile + _runas -u 100 -g 101 -- setfacl -m u::rwx,g::rwx,o::rwx $testfile + +CVE: CVE-2016-7097 +Upstream_status: backport + +Signed-off-by: Gu Zheng +Signed-off-by: Al Viro +Signed-off-by: Jan Kara +Signed-off-by: Jiri Slaby +Signed-off-by: Sona Sarmadi +--- + fs/generic_acl.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/fs/generic_acl.c b/fs/generic_acl.c +index b3f3676..7855cfb 100644 +--- a/fs/generic_acl.c ++++ b/fs/generic_acl.c +@@ -82,19 +82,21 @@ generic_acl_set(struct dentry *dentry, const char *name, const void *value, + return PTR_ERR(acl); + } + if (acl) { ++ struct posix_acl *old_acl; ++ + error = posix_acl_valid(acl); + if (error) + goto failed; + switch (type) { + case ACL_TYPE_ACCESS: +- error = posix_acl_equiv_mode(acl, &inode->i_mode); ++ old_acl = acl; ++ error = posix_acl_update_mode(inode, &inode->i_mode, ++ &acl); + if (error < 0) + goto failed; ++ if (!acl) ++ posix_acl_release(old_acl); + inode->i_ctime = CURRENT_TIME; +- if (error == 0) { +- posix_acl_release(acl); +- acl = NULL; +- } + break; + case ACL_TYPE_DEFAULT: + if (!S_ISDIR(inode->i_mode)) { +-- +cgit v0.12 + diff --git a/recipes-kernel/linux/files/tmpfs-CVE-2016-7097.patch b/recipes-kernel/linux/files/tmpfs-CVE-2016-7097.patch new file mode 100644 index 0000000..b11830c --- /dev/null +++ b/recipes-kernel/linux/files/tmpfs-CVE-2016-7097.patch @@ -0,0 +1,440 @@ +From a88a2be387d1d8a5843826e12009c79e32fb46c8 Mon Sep 17 00:00:00 2001 +From: Jan Kara +Date: Tue, 25 Oct 2016 08:44:26 -0500 +Subject: posix_acl: Clear SGID bit when setting file permissions + +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. + +CVE: CVE-2016-7097 +Upstream-Status: Backport + +Reviewed-by: Christoph Hellwig +Reviewed-by: Jeff Layton +Signed-off-by: Jan Kara +Signed-off-by: Andreas Gruenbacher +Signed-off-by: Jiri Slaby +Signed-off-by: Sona Sarmadi +--- + fs/9p/acl.c | 40 +++++++++++++++++----------------------- + fs/btrfs/acl.c | 6 ++---- + fs/ext2/acl.c | 12 ++++-------- + fs/ext3/acl.c | 10 +++------- + fs/ext4/acl.c | 12 ++++-------- + fs/f2fs/acl.c | 6 ++---- + fs/gfs2/acl.c | 14 ++++++-------- + fs/hfsplus/posix_acl.c | 4 ++-- + fs/hfsplus/xattr.c | 5 +++-- + fs/jffs2/acl.c | 9 ++++----- + fs/jfs/xattr.c | 5 +++-- + fs/ocfs2/acl.c | 20 +++++++------------- + fs/posix_acl.c | 31 +++++++++++++++++++++++++++++++ + fs/reiserfs/xattr_acl.c | 8 ++------ + fs/xfs/xfs_acl.c | 15 +++++++-------- + include/linux/posix_acl.h | 1 + + 16 files changed, 98 insertions(+), 100 deletions(-) + +diff --git a/fs/9p/acl.c b/fs/9p/acl.c +index 7af425f..9686c1f1 100644 +--- a/fs/9p/acl.c ++++ b/fs/9p/acl.c +@@ -320,32 +320,26 @@ static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name, + 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: +diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c +index 0890c83..d6d53e5 100644 +--- a/fs/btrfs/acl.c ++++ b/fs/btrfs/acl.c +@@ -118,11 +118,9 @@ static int btrfs_set_acl(struct btrfs_trans_handle *trans, + 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; +diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c +index 110b6b3..48c3c2d 100644 +--- a/fs/ext2/acl.c ++++ b/fs/ext2/acl.c +@@ -206,15 +206,11 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) + 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/ext3/acl.c b/fs/ext3/acl.c +index dbb5ad5..2f994bb 100644 +--- a/fs/ext3/acl.c ++++ b/fs/ext3/acl.c +@@ -205,15 +205,11 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type, + case ACL_TYPE_ACCESS: + name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; + if (acl) { +- error = posix_acl_equiv_mode(acl, &inode->i_mode); ++ error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (error < 0) + 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; + +diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c +index 39a54a0..c844f1b 100644 +--- a/fs/ext4/acl.c ++++ b/fs/ext4/acl.c +@@ -211,15 +211,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 b7826ec..f4fefc5 100644 +--- a/fs/f2fs/acl.c ++++ b/fs/f2fs/acl.c +@@ -223,12 +223,10 @@ static int f2fs_set_acl(struct inode *inode, int type, struct posix_acl *acl) + 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; + +diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c +index f69ac0a..a61b0c2 100644 +--- a/fs/gfs2/acl.c ++++ b/fs/gfs2/acl.c +@@ -268,15 +268,13 @@ static int gfs2_xattr_system_set(struct dentry *dentry, const char *name, + + if (type == ACL_TYPE_ACCESS) { + umode_t mode = inode->i_mode; +- error = posix_acl_equiv_mode(acl, &mode); ++ struct posix_acl *old_acl = acl; + +- if (error <= 0) { +- posix_acl_release(acl); +- acl = NULL; +- +- if (error < 0) +- return error; +- } ++ error = posix_acl_update_mode(inode, &mode, &acl); ++ if (error < 0) ++ goto out_release; ++ if (!acl) ++ posix_acl_release(old_acl); + + error = gfs2_set_mode(inode, mode); + if (error) +diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c +index b609cc1..9f7cc49 100644 +--- a/fs/hfsplus/posix_acl.c ++++ b/fs/hfsplus/posix_acl.c +@@ -72,8 +72,8 @@ static int hfsplus_set_posix_acl(struct inode *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; +diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c +index bd8471f..889be3f 100644 +--- a/fs/hfsplus/xattr.c ++++ b/fs/hfsplus/xattr.c +@@ -69,8 +69,9 @@ static int can_set_system_xattr(struct inode *inode, const char *name, + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { +- err = posix_acl_equiv_mode(acl, &inode->i_mode); +- posix_acl_release(acl); ++ struct posix_acl *old_acl = acl; ++ err = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ posix_acl_release(old_acl); + if (err < 0) + return err; + mark_inode_dirty(inode); +diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c +index 223283c..9335b8d 100644 +--- a/fs/jffs2/acl.c ++++ b/fs/jffs2/acl.c +@@ -243,9 +243,10 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) + 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; +@@ -257,8 +258,6 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) + if (rc < 0) + return rc; + } +- if (rc == 0) +- acl = NULL; + } + break; + case ACL_TYPE_DEFAULT: +diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c +index d3472f4..8c9b6a0 100644 +--- a/fs/jfs/xattr.c ++++ b/fs/jfs/xattr.c +@@ -693,8 +693,9 @@ static int can_set_system_xattr(struct inode *inode, const char *name, + return rc; + } + if (acl) { +- rc = posix_acl_equiv_mode(acl, &inode->i_mode); +- posix_acl_release(acl); ++ struct posix_acl *old_acl = acl; ++ rc = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ posix_acl_release(old_acl); + if (rc < 0) { + printk(KERN_ERR + "posix_acl_equiv_mode returned %d\n", +diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c +index b4f788e..23095b0 100644 +--- a/fs/ocfs2/acl.c ++++ b/fs/ocfs2/acl.c +@@ -270,20 +270,14 @@ static 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; ++ ret = ocfs2_acl_set_mode(inode, di_bh, ++ handle, mode); ++ if (ret) + return ret; +- else { +- if (ret == 0) +- acl = NULL; +- +- ret = ocfs2_acl_set_mode(inode, di_bh, +- handle, mode); +- if (ret) +- return ret; +- +- } + } + break; + case ACL_TYPE_DEFAULT: +diff --git a/fs/posix_acl.c b/fs/posix_acl.c +index 3542f1f..1da000a 100644 +--- a/fs/posix_acl.c ++++ b/fs/posix_acl.c +@@ -407,6 +407,37 @@ posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) + } + EXPORT_SYMBOL(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); ++ + int + posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode) + { +diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c +index 06c04f7..a86ad7e 100644 +--- a/fs/reiserfs/xattr_acl.c ++++ b/fs/reiserfs/xattr_acl.c +@@ -288,13 +288,9 @@ reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode, + 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: +diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c +index 0e2f37e..9c7b5ce 100644 +--- a/fs/xfs/xfs_acl.c ++++ b/fs/xfs/xfs_acl.c +@@ -402,16 +402,15 @@ xfs_xattr_acl_set(struct dentry *dentry, const char *name, + goto out_release; + + if (type == ACL_TYPE_ACCESS) { +- umode_t mode = inode->i_mode; +- error = posix_acl_equiv_mode(acl, &mode); ++ umode_t mode; ++ struct posix_acl *old_acl = acl; + +- if (error <= 0) { +- posix_acl_release(acl); +- acl = NULL; ++ error = posix_acl_update_mode(inode, &mode, &acl); + +- if (error < 0) +- return error; +- } ++ if (error) ++ goto out_release; ++ if (!acl) ++ posix_acl_release(old_acl); + + error = xfs_set_mode(inode, mode); + if (error) +diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h +index 7931efe..43cb8d5 100644 +--- a/include/linux/posix_acl.h ++++ b/include/linux/posix_acl.h +@@ -89,6 +89,7 @@ extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); + extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t); + extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *); + extern int posix_acl_create(struct posix_acl **, gfp_t, umode_t *); ++extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **); + extern int posix_acl_chmod(struct posix_acl **, gfp_t, umode_t); + + extern struct posix_acl *get_posix_acl(struct inode *, int); +-- +cgit v0.12 + diff --git a/recipes-kernel/linux/linux-qoriq_3.12.bbappend b/recipes-kernel/linux/linux-qoriq_3.12.bbappend index be8cf93..7689676 100644 --- a/recipes-kernel/linux/linux-qoriq_3.12.bbappend +++ b/recipes-kernel/linux/linux-qoriq_3.12.bbappend @@ -10,5 +10,6 @@ SRC_URI += "file://ppp-CVE-2015-8569.patch \ file://CVE-2016-5195.patch \ file://CVE-2016-6480.patch \ file://ring-buffer-CVE-2016-9754.patch \ + file://tmpfs-CVE-2016-7097.patch \ " -- cgit v1.2.3-54-g00ecf