posix_acl: Clear SGID bit when setting file permissions

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.

Change-Id: Ibd68b4b3a43a463b55032442a879219001008f46
This commit is contained in:
Jan Kara 2017-06-16 04:14:46 +02:00 committed by syphyr
parent 0a98a537be
commit bdc15f09d9
6 changed files with 31 additions and 41 deletions

View 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;

View File

@ -267,14 +267,14 @@ static int gfs2_xattr_system_set(struct dentry *dentry, const char *name,
goto out_release;
if (type == ACL_TYPE_ACCESS) {
umode_t mode;
umode_t mode = inode->i_mode;
struct posix_acl *old_acl = acl;
error = posix_acl_update_mode(inode, &mode, &acl);
if (error < 0)
goto out_release;
if (!acl)
posix_acl_release(old_acl);
if (error)
goto out_release;
error = gfs2_set_mode(inode, mode);
if (error)

View File

@ -694,7 +694,6 @@ static int can_set_system_xattr(struct inode *inode, const char *name,
}
if (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) {

View File

@ -275,19 +275,13 @@ static int ocfs2_set_acl(handle_t *handle,
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
umode_t mode;
ret = posix_acl_update_mode(inode, &mode, &acl);
if (ret)
return ret;
else {
if (ret == 0)
acl = NULL;
ret = ocfs2_acl_set_mode(inode, di_bh,
handle, mode);
if (ret)
return ret;
}
ret = ocfs2_acl_set_mode(inode, di_bh,
handle, mode);
if (ret)
return ret;
}
break;
case ACL_TYPE_DEFAULT:

View File

@ -407,24 +407,6 @@ posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
}
EXPORT_SYMBOL(posix_acl_create);
int
posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
{
struct posix_acl *clone = posix_acl_clone(*acl, gfp);
int err = -ENOMEM;
if (clone) {
err = posix_acl_chmod_masq(clone, mode);
if (err) {
posix_acl_release(clone);
clone = NULL;
}
}
posix_acl_release(*acl);
*acl = clone;
return err;
}
EXPORT_SYMBOL(posix_acl_chmod);
/**
* posix_acl_update_mode - update mode in set_acl
*
@ -455,3 +437,21 @@ int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
return 0;
}
EXPORT_SYMBOL(posix_acl_update_mode);
int
posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
{
struct posix_acl *clone = posix_acl_clone(*acl, gfp);
int err = -ENOMEM;
if (clone) {
err = posix_acl_chmod_masq(clone, mode);
if (err) {
posix_acl_release(clone);
clone = NULL;
}
}
posix_acl_release(*acl);
*acl = clone;
return err;
}
EXPORT_SYMBOL(posix_acl_chmod);

View File

@ -392,10 +392,11 @@ xfs_xattr_acl_set(struct dentry *dentry, const char *name,
struct posix_acl *old_acl = acl;
error = posix_acl_update_mode(inode, &mode, &acl);
if (!acl)
posix_acl_release(old_acl);
if (error)
goto out_release;
if (!acl)
posix_acl_release(old_acl);
error = xfs_set_mode(inode, mode);
if (error)