]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.8-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 28 Oct 2016 15:41:29 +0000 (11:41 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 28 Oct 2016 15:41:29 +0000 (11:41 -0400)
added patches:
posix_acl-clear-sgid-bit-when-setting-file-permissions.patch

queue-4.8/posix_acl-clear-sgid-bit-when-setting-file-permissions.patch [new file with mode: 0644]
queue-4.8/series

diff --git a/queue-4.8/posix_acl-clear-sgid-bit-when-setting-file-permissions.patch b/queue-4.8/posix_acl-clear-sgid-bit-when-setting-file-permissions.patch
new file mode 100644 (file)
index 0000000..f5d1237
--- /dev/null
@@ -0,0 +1,408 @@
+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
+
+From: Jan Kara <jack@suse.cz>
+
+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
+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>
+Signed-off-by: Juerg Haefliger <juerg.haefliger@hpe.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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
+@@ -79,11 +79,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
+@@ -210,12 +210,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(inode, 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
+@@ -73,14 +73,11 @@ int orangefs_set_acl(struct inode *inode
+       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
+                               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 *);
index 26cf8fb431415007ba0377606fa9f0d9130ab6f9..5cef7878f5462a9b2e16f92487eed384a668be8e 100644 (file)
@@ -36,3 +36,4 @@ drm-i915-move-long-hpd-handling-into-the-hotplug-work.patch
 drm-i915-allow-dp-to-work-w-o-edid.patch
 drm-i915-just-clear-the-mmiodebug-before-a-register-access.patch
 drm-i915-unalias-obj-phys_handle-and-obj-userptr.patch
+posix_acl-clear-sgid-bit-when-setting-file-permissions.patch