mirror of
https://github.com/torvalds/linux.git
synced 2024-11-13 07:31:45 +00:00
posix_acl: Clear SGID bit when setting file permissions
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>
This commit is contained in:
parent
5d3ddd84ea
commit
073931017b
40
fs/9p/acl.c
40
fs/9p/acl.c
@ -276,32 +276,26 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
|
|||||||
switch (handler->flags) {
|
switch (handler->flags) {
|
||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
if (acl) {
|
if (acl) {
|
||||||
umode_t mode = inode->i_mode;
|
struct iattr iattr;
|
||||||
retval = posix_acl_equiv_mode(acl, &mode);
|
|
||||||
if (retval < 0)
|
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
|
||||||
|
if (retval)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
else {
|
if (!acl) {
|
||||||
struct iattr iattr;
|
/*
|
||||||
if (retval == 0) {
|
* ACL can be represented
|
||||||
/*
|
* by the mode bits. So don't
|
||||||
* ACL can be represented
|
* update ACL.
|
||||||
* 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 ?
|
|
||||||
*/
|
*/
|
||||||
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;
|
break;
|
||||||
case ACL_TYPE_DEFAULT:
|
case ACL_TYPE_DEFAULT:
|
||||||
|
@ -79,11 +79,9 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
ret = posix_acl_equiv_mode(acl, &inode->i_mode);
|
ret = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
if (ret == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -95,11 +95,9 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
ret = posix_acl_equiv_mode(acl, &new_mode);
|
ret = posix_acl_update_mode(inode, &new_mode, &acl);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
if (ret == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ACL_TYPE_DEFAULT:
|
case ACL_TYPE_DEFAULT:
|
||||||
|
@ -190,15 +190,11 @@ ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||||
if (error < 0)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
else {
|
inode->i_ctime = CURRENT_TIME_SEC;
|
||||||
inode->i_ctime = CURRENT_TIME_SEC;
|
mark_inode_dirty(inode);
|
||||||
mark_inode_dirty(inode);
|
|
||||||
if (error == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -193,15 +193,11 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
|
name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||||
if (error < 0)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
else {
|
inode->i_ctime = ext4_current_time(inode);
|
||||||
inode->i_ctime = ext4_current_time(inode);
|
ext4_mark_inode_dirty(handle, inode);
|
||||||
ext4_mark_inode_dirty(handle, inode);
|
|
||||||
if (error == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -210,12 +210,10 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
|
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||||
if (error < 0)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
set_acl_inode(inode, inode->i_mode);
|
set_acl_inode(inode, inode->i_mode);
|
||||||
if (error == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -92,17 +92,11 @@ int __gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
if (type == ACL_TYPE_ACCESS) {
|
if (type == ACL_TYPE_ACCESS) {
|
||||||
umode_t mode = inode->i_mode;
|
umode_t mode = inode->i_mode;
|
||||||
|
|
||||||
error = posix_acl_equiv_mode(acl, &mode);
|
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||||
if (error < 0)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
if (mode != inode->i_mode)
|
||||||
if (error == 0)
|
|
||||||
acl = NULL;
|
|
||||||
|
|
||||||
if (mode != inode->i_mode) {
|
|
||||||
inode->i_mode = mode;
|
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acl) {
|
if (acl) {
|
||||||
|
@ -65,8 +65,8 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
err = posix_acl_equiv_mode(acl, &inode->i_mode);
|
err = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||||
if (err < 0)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -233,9 +233,10 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
|
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
umode_t mode = inode->i_mode;
|
umode_t mode;
|
||||||
rc = posix_acl_equiv_mode(acl, &mode);
|
|
||||||
if (rc < 0)
|
rc = posix_acl_update_mode(inode, &mode, &acl);
|
||||||
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
if (inode->i_mode != mode) {
|
if (inode->i_mode != mode) {
|
||||||
struct iattr attr;
|
struct iattr attr;
|
||||||
@ -247,8 +248,6 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
if (rc == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ACL_TYPE_DEFAULT:
|
case ACL_TYPE_DEFAULT:
|
||||||
|
@ -78,13 +78,11 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
rc = posix_acl_equiv_mode(acl, &inode->i_mode);
|
rc = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||||
if (rc < 0)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
inode->i_ctime = CURRENT_TIME;
|
inode->i_ctime = CURRENT_TIME;
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
if (rc == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ACL_TYPE_DEFAULT:
|
case ACL_TYPE_DEFAULT:
|
||||||
|
@ -241,13 +241,11 @@ int ocfs2_set_acl(handle_t *handle,
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
umode_t mode = inode->i_mode;
|
umode_t mode;
|
||||||
ret = posix_acl_equiv_mode(acl, &mode);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (ret == 0)
|
ret = posix_acl_update_mode(inode, &mode, &acl);
|
||||||
acl = NULL;
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = ocfs2_acl_set_mode(inode, di_bh,
|
ret = ocfs2_acl_set_mode(inode, di_bh,
|
||||||
handle, mode);
|
handle, mode);
|
||||||
|
@ -73,14 +73,11 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
umode_t mode = inode->i_mode;
|
umode_t mode;
|
||||||
/*
|
|
||||||
* can we represent this with the traditional file
|
error = posix_acl_update_mode(inode, &mode, &acl);
|
||||||
* mode permission bits?
|
if (error) {
|
||||||
*/
|
gossip_err("%s: posix_acl_update_mode err: %d\n",
|
||||||
error = posix_acl_equiv_mode(acl, &mode);
|
|
||||||
if (error < 0) {
|
|
||||||
gossip_err("%s: posix_acl_equiv_mode err: %d\n",
|
|
||||||
__func__,
|
__func__,
|
||||||
error);
|
error);
|
||||||
return error;
|
return error;
|
||||||
@ -90,8 +87,6 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
SetModeFlag(orangefs_inode);
|
SetModeFlag(orangefs_inode);
|
||||||
inode->i_mode = mode;
|
inode->i_mode = mode;
|
||||||
mark_inode_dirty_sync(inode);
|
mark_inode_dirty_sync(inode);
|
||||||
if (error == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ACL_TYPE_DEFAULT:
|
case ACL_TYPE_DEFAULT:
|
||||||
|
@ -626,6 +626,37 @@ no_mem:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(posix_acl_create);
|
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.
|
* Fix up the uids and gids in posix acl extended attributes in place.
|
||||||
*/
|
*/
|
||||||
|
@ -242,13 +242,9 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
|
|||||||
case ACL_TYPE_ACCESS:
|
case ACL_TYPE_ACCESS:
|
||||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||||
if (acl) {
|
if (acl) {
|
||||||
error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||||
if (error < 0)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
else {
|
|
||||||
if (error == 0)
|
|
||||||
acl = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ACL_TYPE_DEFAULT:
|
case ACL_TYPE_DEFAULT:
|
||||||
|
@ -257,16 +257,11 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (type == ACL_TYPE_ACCESS) {
|
if (type == ACL_TYPE_ACCESS) {
|
||||||
umode_t mode = inode->i_mode;
|
umode_t mode;
|
||||||
error = posix_acl_equiv_mode(acl, &mode);
|
|
||||||
|
|
||||||
if (error <= 0) {
|
|
||||||
acl = NULL;
|
|
||||||
|
|
||||||
if (error < 0)
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
error = posix_acl_update_mode(inode, &mode, &acl);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
error = xfs_set_mode(inode, mode);
|
error = xfs_set_mode(inode, mode);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
@ -93,6 +93,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_chmod(struct inode *, umode_t);
|
||||||
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
|
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
|
||||||
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_set_acl(struct inode *, struct posix_acl *, int);
|
||||||
extern int simple_acl_create(struct inode *, struct inode *);
|
extern int simple_acl_create(struct inode *, struct inode *);
|
||||||
|
Loading…
Reference in New Issue
Block a user