Re: [PATCH v2 RESEND] posix_acl: Clear SGID bit when setting file permissions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 2016-09-19 at 17:42 +0200, Jan Kara wrote:
> 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
> > Signed-off-by: Jan Kara <jack@xxxxxxx>
> > Signed-off-by: Andreas Gruenbacher <agruenba@xxxxxxxxxx>
> ---
>  fs/9p/acl.c               | 40 +++++++++++++++++-----------------------
>  fs/btrfs/acl.c            |  6 ++----
>  fs/ceph/acl.c             |  6 ++----
>  fs/ext2/acl.c             | 12 ++++--------
>  fs/ext4/acl.c             | 12 ++++--------
>  fs/f2fs/acl.c             |  6 ++----
>  fs/gfs2/acl.c             | 12 +++---------
>  fs/hfsplus/posix_acl.c    |  4 ++--
>  fs/jffs2/acl.c            |  9 ++++-----
>  fs/jfs/acl.c              |  6 ++----
>  fs/ocfs2/acl.c            | 10 ++++------
>  fs/orangefs/acl.c         | 15 +++++----------
>  fs/posix_acl.c            | 31 +++++++++++++++++++++++++++++++
>  fs/reiserfs/xattr_acl.c   |  8 ++------
>  fs/xfs/xfs_acl.c          | 13 ++++---------
>  include/linux/posix_acl.h |  1 +
>  16 files changed, 89 insertions(+), 102 deletions(-)
> 
> Another forgotten patch. It was posted on May 27 and Aug 19. Al, can you
> please pick it up? Andrew, if Al does not respond, can you please take care
> of pushing it to Linus? Thanks!
> 
> diff --git a/fs/9p/acl.c b/fs/9p/acl.c
> index 5b6a1743ea17..b3c2cc79c20d 100644
> --- a/fs/9p/acl.c
> +++ b/fs/9p/acl.c
> @@ -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:
> diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
> index 53bb7af4e5f0..247b8dfaf6e5 100644
> --- a/fs/btrfs/acl.c
> +++ b/fs/btrfs/acl.c
> @@ -79,11 +79,9 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
> >  	case ACL_TYPE_ACCESS:
> >  		name = XATTR_NAME_POSIX_ACL_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;
> diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
> index 4f67227f69a5..d0b6b342dff9 100644
> --- a/fs/ceph/acl.c
> +++ b/fs/ceph/acl.c
> @@ -95,11 +95,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:
> diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
> index 42f1d1814083..e725aa0890e0 100644
> --- a/fs/ext2/acl.c
> +++ b/fs/ext2/acl.c
> @@ -190,15 +190,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;
>  
> diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
> index c6601a476c02..dfa519979038 100644
> --- a/fs/ext4/acl.c
> +++ b/fs/ext4/acl.c
> @@ -193,15 +193,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;
>  
> diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
> index 4dcc9e28dc5c..31344247ce89 100644
> --- a/fs/f2fs/acl.c
> +++ b/fs/f2fs/acl.c
> @@ -210,12 +210,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(inode, inode->i_mode);
> > -			if (error == 0)
> > -				acl = NULL;
> >  		}
> >  		break;
>  
> diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
> index 363ba9e9d8d0..2524807ee070 100644
> --- a/fs/gfs2/acl.c
> +++ b/fs/gfs2/acl.c
> @@ -92,17 +92,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) {
> diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c
> index ab7ea2506b4d..9b92058a1240 100644
> --- a/fs/hfsplus/posix_acl.c
> +++ b/fs/hfsplus/posix_acl.c
> @@ -65,8 +65,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;
> diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
> index bc2693d56298..2a0f2a1044c1 100644
> --- a/fs/jffs2/acl.c
> +++ b/fs/jffs2/acl.c
> @@ -233,9 +233,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;
> @@ -247,8 +248,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:
> diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
> index 21fa92ba2c19..3a1e1554a4e3 100644
> --- a/fs/jfs/acl.c
> +++ b/fs/jfs/acl.c
> @@ -78,13 +78,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:
> diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
> index 2162434728c0..164307b99405 100644
> --- a/fs/ocfs2/acl.c
> +++ b/fs/ocfs2/acl.c
> @@ -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
> index 28f2195cd798..7a3754488312 100644
> --- a/fs/orangefs/acl.c
> +++ b/fs/orangefs/acl.c
> @@ -73,14 +73,11 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
> >  	case ACL_TYPE_ACCESS:
> >  		name = XATTR_NAME_POSIX_ACL_ACCESS;
> >  		if (acl) {
> > -			umode_t mode = inode->i_mode;
> > -			/*
> > -			 * can we represent this with the traditional file
> > -			 * mode permission bits?
> > -			 */
> > -			error = posix_acl_equiv_mode(acl, &mode);
> > -			if (error < 0) {
> > -				gossip_err("%s: posix_acl_equiv_mode err: %d\n",
> > +			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;
> @@ -90,8 +87,6 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
> >  				SetModeFlag(orangefs_inode);
> >  			inode->i_mode = mode;
> >  			mark_inode_dirty_sync(inode);
> > -			if (error == 0)
> > -				acl = NULL;
> >  		}
> >  		break;
> >  	case ACL_TYPE_DEFAULT:
> diff --git a/fs/posix_acl.c b/fs/posix_acl.c
> index 59d47ab0791a..bfc3ec388322 100644
> --- a/fs/posix_acl.c
> +++ b/fs/posix_acl.c
> @@ -626,6 +626,37 @@ no_mem:
>  }
>  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.
>   */
> diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
> index dbed42f755e0..27376681c640 100644
> --- a/fs/reiserfs/xattr_acl.c
> +++ b/fs/reiserfs/xattr_acl.c
> @@ -242,13 +242,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:
> diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
> index b6e527b8eccb..8a0dec89ca56 100644
> --- a/fs/xfs/xfs_acl.c
> +++ b/fs/xfs/xfs_acl.c
> @@ -257,16 +257,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;
> diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
> index d5d3d741f028..bf1046d0397b 100644
> --- a/include/linux/posix_acl.h
> +++ b/include/linux/posix_acl.h
> @@ -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_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 *);

Looks correct:

Reviewed-by: Jeff Layton <jlayton@xxxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux