]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
posix_acl: Clear SGID bit when setting file permissions
authorJan Kara <jack@suse.cz>
Mon, 19 Sep 2016 15:39:09 +0000 (17:39 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 8 May 2017 05:44:10 +0000 (07:44 +0200)
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: Amit Pundir <amit.pundir@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
15 files changed:
fs/9p/acl.c
fs/btrfs/acl.c
fs/ceph/acl.c
fs/ext2/acl.c
fs/ext4/acl.c
fs/f2fs/acl.c
fs/gfs2/acl.c
fs/hfsplus/posix_acl.c
fs/jffs2/acl.c
fs/jfs/acl.c
fs/ocfs2/acl.c
fs/posix_acl.c
fs/reiserfs/xattr_acl.c
fs/xfs/xfs_acl.c
include/linux/posix_acl.h

index 8482f2d1160667ba1255ee5b48434287bc51da65..d3f5d487ae466c65e1af8e4d5d1cd26bc631fca5 100644 (file)
@@ -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:
index 9a0124a95851014c9000ed7822dc5fad0a2f6995..fb3e64d37cb4ca6d2faf4fadefd85b1477a95f2e 100644 (file)
@@ -83,11 +83,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;
index 5bd853ba44ffccfc39b45b511e6f3b31bd3c1e37..6a4a3e2a46cf2058ad6f91414441dfeb3440180c 100644 (file)
@@ -108,11 +108,9 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
        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:
index 27695e6f4e46629a8a888f38bcaf6f0e4ceaf1ec..d6aeb84e90b61acb8dff22443b9a9694c7ba3dba 100644 (file)
@@ -193,15 +193,11 @@ ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
                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;
 
index d40c8dbbb0d66a3622915c68dc104841ab1f5753..87d9bbf6a53f3d6ea8c2f77bcd5e317a0893d103 100644 (file)
@@ -201,15 +201,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;
 
index 83b9b5a8d11203d2a2f6749a2841d0473752a5a2..f12d5c5ecc31b052c1a10268099f611db84d767a 100644 (file)
@@ -207,12 +207,10 @@ static int __f2fs_set_acl(struct inode *inode, int type,
        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;
 
index 7b3143064af1130d22c14f66c8236785093c545a..88e66aa516c4727439cc49cc86c299b1e7b8d8d1 100644 (file)
@@ -79,17 +79,11 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
        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) {
index df0c9af68d05ef0df5a127d53562677bc8a2d882..71b3087b7e327db93192252daa9b3cb15a2ef8c1 100644 (file)
@@ -68,8 +68,8 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
        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;
index 2f7a3c09048999f4365c3e5612ffdf18e9c65d54..f9f86f87d32b82ddb7b78ba30c4270f3caac9a8a 100644 (file)
@@ -235,9 +235,10 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
        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;
@@ -249,8 +250,6 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
                                if (rc < 0)
                                        return rc;
                        }
-                       if (rc == 0)
-                               acl = NULL;
                }
                break;
        case ACL_TYPE_DEFAULT:
index 0c8ca830b113e62246d3f2629846958bdee32775..9fad9f4fe8830f02dcb9ebe02d8052ab307f97e0 100644 (file)
@@ -84,13 +84,11 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
        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:
index 8a7d2f812b5be4f37058c6360f47e7e1073691aa..c7641f656494dbc777f632670631a68032260716 100644 (file)
@@ -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:
index 3de7c223c963869fc204ca19fdfecf0f2b9d716f..38c91932eb525095e3c24b9318668f2ac8f30ce1 100644 (file)
@@ -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.
  */
index 4b34b9dc03dda9fffd8da5d3ab7221bd9ab139b7..9b1824f355016a9ab070d716ff40d75f55417837 100644 (file)
@@ -246,13 +246,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:
index a65fa5dde6e9c514b0e5fbfeecd6d14a83e202bc..e0406717edbc613629d257392c0563782087339c 100644 (file)
@@ -286,16 +286,11 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
                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;
index 3e96a6a7610338ff8ddd825a21ddc9bc5fe52e8b..d1a8ad7e5ae450b38c8d83ed2e74327424cfa79f 100644 (file)
@@ -95,6 +95,7 @@ extern int set_posix_acl(struct inode *, int, struct posix_acl *);
 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 *);