posix_acl: Clear SGID bit when setting file permissions
authorJan Kara <jack@suse.cz>
Tue, 25 Oct 2016 13:44:26 +0000 (08:44 -0500)
committerWilly Tarreau <w@1wt.eu>
Wed, 7 Jun 2017 22:46:47 +0000 (00:46 +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.

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>
[wt: dropped hfsplus changes : no xattr in 3.10]
Signed-off-by: Willy Tarreau <w@1wt.eu>
14 files changed:
fs/9p/acl.c
fs/btrfs/acl.c
fs/ext2/acl.c
fs/ext3/acl.c
fs/ext4/acl.c
fs/f2fs/acl.c
fs/gfs2/acl.c
fs/jffs2/acl.c
fs/jfs/xattr.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 7af425f53beef91a6d21dc86b76e161ff7ed1696..9686c1f17653f4469156ea19d83a1ceaeef763da 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 0890c83643e944f69e43a22f4151e381e7503f04..d6d53e5e7945db193e1fec7cdfbb6b230b916542 100644 (file)
@@ -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;
index 110b6b371a4edc353fc1904bcc79299580087184..48c3c2d7d2619e4c27121de46d9d8dc8c9dbb391 100644 (file)
@@ -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;
 
index dbb5ad59a7fc3c22380ce30b9034f8942bec967b..2f994bbf73a7948cac159d3b1a61178765c71692 100644 (file)
@@ -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;
 
index 39a54a0e9fe4a9a6290f75b961cde9ccea06f669..c844f1bfb451920f896269f05d511eb46b696cba 100644 (file)
@@ -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;
 
index 44abc2f286e00ad4ecff03e0293f80ab8d3d19fd..9c4f3c732bce4a3a6773c848c1a05eb25c7cb1d1 100644 (file)
@@ -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;
 
index f69ac0af5496cd456bf6df4d0c932747b6767779..a61b0c2b57ab047ada5a0fc1194ac5533a75f2c0 100644 (file)
@@ -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)
index 223283c301116f3d8d88cb70cf30aa030a54c456..9335b8d3cf5212cede3093af48bf37567400623d 100644 (file)
@@ -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:
index 42d67f9757bf641d316b07e78a3fb1a698f76409..29a28601cb93eb0d8c39098d95f0e92ab16ed562 100644 (file)
@@ -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",
index 8a404576fb26557eb8578e21f66d0277a94a9e76..51ff9506cb0f3647ebc294e917271b9eb6930688 100644 (file)
@@ -274,20 +274,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:
index 3542f1f814e2a966d33429f61e9f78ab5404a18c..1da000aabb0898a9fe06daa903c1fa18463098e8 100644 (file)
@@ -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)
 {
index 6c8767fdfc6a287b0a9d201b4048d89b06fecaad..2d73589f37d643fe2a38ab76188cc92e2c4c91d6 100644 (file)
@@ -286,13 +286,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 306d883d89bc7d6420ca4b5b8c5f848e573249bf..5e9a9a62a45c146cb51ea216c59084ba5dad9335 100644 (file)
@@ -388,16 +388,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)
index 7931efe7117553d00a920cfd4aef757f72578658..43cb8d59d0a77e52fdf6edf93f65d0e5935b5cf6 100644 (file)
@@ -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);