]> git.hungrycats.org Git - linux/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)
committerZygo Blaxell <zblaxell@thirteen.furryterror.org>
Sun, 27 Nov 2016 18:22:03 +0000 (13:22 -0500)
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>
(cherry picked from commit ea288a690cc4e53a528ae6a1d37cd6e14320ed27)

Conflicts:
fs/f2fs/acl.c
fs/orangefs/acl.c

12 files changed:
fs/9p/acl.c
fs/ceph/acl.c
fs/ext2/acl.c
fs/ext4/acl.c
fs/gfs2/acl.c
fs/hfsplus/posix_acl.c
fs/jffs2/acl.c
fs/jfs/acl.c
fs/ocfs2/acl.c
fs/orangefs/acl.c [new file with mode: 0644]
fs/reiserfs/xattr_acl.c
fs/xfs/xfs_acl.c

index 9da967f383872dab4bc08ed149a230f5f8304f1a..d525adc202b56413fb3d46d09aad12892902c090 100644 (file)
@@ -276,32 +276,26 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
        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:
index f19708487e2f74c80e55b14ecbadb07bf79b73a9..8f81392f9c82784b1a11aabbc3ce65bf92c4639a 100644 (file)
@@ -94,11 +94,9 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
        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:
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 69b1e73026a51f6b8ae0e591d33abb0fdc9169d8..c3fe1e323951f9da0d19aa3eeb333c119f59a142 100644 (file)
@@ -196,15 +196,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 791932617d1a39972aee07a87ed35e8597e6b7bd..5b03a41c6e0f5b07bc4997635972949f0fa18265 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 afb33eda6d7dbae7ea5dab4d19b1edc401db151a..de71351aa4b9c3cbf2e62439f72977717861786a 100644 (file)
@@ -68,8 +68,8 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
        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;
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 49456853e9de230822b4cb97b407c565525d2ffe..92b25809146d5f351ff245a936baf8842fccf721 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 = 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:
index 2162434728c022ab4651904b778c21d958ca802d..164307b994052cb658b08cb8c28da524dedfe644 100644 (file)
@@ -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);
diff --git a/fs/orangefs/acl.c b/fs/orangefs/acl.c
new file mode 100644 (file)
index 0000000..7a37544
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * (C) 2001 Clemson University and The University of Chicago
+ *
+ * See COPYING in top-level directory.
+ */
+
+#include "protocol.h"
+#include "orangefs-kernel.h"
+#include "orangefs-bufmap.h"
+#include <linux/posix_acl_xattr.h>
+#include <linux/fs_struct.h>
+
+struct posix_acl *orangefs_get_acl(struct inode *inode, int type)
+{
+       struct posix_acl *acl;
+       int ret;
+       char *key = NULL, *value = NULL;
+
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               key = XATTR_NAME_POSIX_ACL_ACCESS;
+               break;
+       case ACL_TYPE_DEFAULT:
+               key = XATTR_NAME_POSIX_ACL_DEFAULT;
+               break;
+       default:
+               gossip_err("orangefs_get_acl: bogus value of type %d\n", type);
+               return ERR_PTR(-EINVAL);
+       }
+       /*
+        * Rather than incurring a network call just to determine the exact
+        * length of the attribute, I just allocate a max length to save on
+        * the network call. Conceivably, we could pass NULL to
+        * orangefs_inode_getxattr() to probe the length of the value, but
+        * I don't do that for now.
+        */
+       value = kmalloc(ORANGEFS_MAX_XATTR_VALUELEN, GFP_KERNEL);
+       if (value == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       gossip_debug(GOSSIP_ACL_DEBUG,
+                    "inode %pU, key %s, type %d\n",
+                    get_khandle_from_ino(inode),
+                    key,
+                    type);
+       ret = orangefs_inode_getxattr(inode, key, value,
+                                     ORANGEFS_MAX_XATTR_VALUELEN);
+       /* if the key exists, convert it to an in-memory rep */
+       if (ret > 0) {
+               acl = posix_acl_from_xattr(&init_user_ns, value, ret);
+       } else if (ret == -ENODATA || ret == -ENOSYS) {
+               acl = NULL;
+       } else {
+               gossip_err("inode %pU retrieving acl's failed with error %d\n",
+                          get_khandle_from_ino(inode),
+                          ret);
+               acl = ERR_PTR(ret);
+       }
+       /* kfree(NULL) is safe, so don't worry if value ever got used */
+       kfree(value);
+       return acl;
+}
+
+int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+{
+       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
+       int error = 0;
+       void *value = NULL;
+       size_t size = 0;
+       const char *name = NULL;
+
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               name = XATTR_NAME_POSIX_ACL_ACCESS;
+               if (acl) {
+                       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;
+                       }
+
+                       if (inode->i_mode != mode)
+                               SetModeFlag(orangefs_inode);
+                       inode->i_mode = mode;
+                       mark_inode_dirty_sync(inode);
+               }
+               break;
+       case ACL_TYPE_DEFAULT:
+               name = XATTR_NAME_POSIX_ACL_DEFAULT;
+               break;
+       default:
+               gossip_err("%s: invalid type %d!\n", __func__, type);
+               return -EINVAL;
+       }
+
+       gossip_debug(GOSSIP_ACL_DEBUG,
+                    "%s: inode %pU, key %s type %d\n",
+                    __func__, get_khandle_from_ino(inode),
+                    name,
+                    type);
+
+       if (acl) {
+               size = posix_acl_xattr_size(acl->a_count);
+               value = kmalloc(size, GFP_KERNEL);
+               if (!value)
+                       return -ENOMEM;
+
+               error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
+               if (error < 0)
+                       goto out;
+       }
+
+       gossip_debug(GOSSIP_ACL_DEBUG,
+                    "%s: name %s, value %p, size %zd, acl %p\n",
+                    __func__, name, value, size, acl);
+       /*
+        * Go ahead and set the extended attribute now. NOTE: Suppose acl
+        * was NULL, then value will be NULL and size will be 0 and that
+        * will xlate to a removexattr. However, we don't want removexattr
+        * complain if attributes does not exist.
+        */
+       error = orangefs_inode_setxattr(inode, name, value, size, 0);
+
+out:
+       kfree(value);
+       if (!error)
+               set_cached_acl(inode, type, acl);
+       return error;
+}
+
+int orangefs_init_acl(struct inode *inode, struct inode *dir)
+{
+       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
+       struct posix_acl *default_acl, *acl;
+       umode_t mode = inode->i_mode;
+       int error = 0;
+
+       ClearModeFlag(orangefs_inode);
+
+       error = posix_acl_create(dir, &mode, &default_acl, &acl);
+       if (error)
+               return error;
+
+       if (default_acl) {
+               error = orangefs_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
+               posix_acl_release(default_acl);
+       }
+
+       if (acl) {
+               if (!error)
+                       error = orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
+               posix_acl_release(acl);
+       }
+
+       /* If mode of the inode was changed, then do a forcible ->setattr */
+       if (mode != inode->i_mode) {
+               SetModeFlag(orangefs_inode);
+               inode->i_mode = mode;
+               orangefs_flush_inode(inode);
+       }
+
+       return error;
+}
index 558a16beaacb994749e74cf74acf869f611a1064..5d2d44fbfde53cdfdafc91e72e2715e632d9d936 100644 (file)
@@ -246,13 +246,9 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
        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:
index 2d5df1f23bbcbe47cebc087023d04126230cbe6c..45cdcd4d90cd1e309548ce14e1899fd49f7e715c 100644 (file)
@@ -265,16 +265,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;