From: Jeff Layton <jlayton@redhat.com>
To: Jan Kara <jack@suse.cz>, Al Viro <viro@ZenIV.linux.org.uk>
Cc: Andrew Morton <akpm@linux-foundation.org>,
linux-fsdevel@vger.kernel.org,
Andreas Gruenbacher <agruenba@redhat.com>
Subject: Re: [PATCH v2 RESEND] posix_acl: Clear SGID bit when setting file permissions
Date: Tue, 20 Sep 2016 12:54:18 -0400 [thread overview]
Message-ID: <1474390458.19989.37.camel@redhat.com> (raw)
In-Reply-To: <1474299768-15150-1-git-send-email-jack@suse.cz>
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@suse.cz>
> > Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
> ---
> 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@redhat.com>
next prev parent reply other threads:[~2016-09-20 16:54 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-09-19 15:42 [PATCH v2 RESEND] posix_acl: Clear SGID bit when setting file permissions Jan Kara
2016-09-20 16:13 ` Christoph Hellwig
2016-09-22 9:10 ` Jan Kara
2016-09-20 16:54 ` Jeff Layton [this message]
2016-10-11 23:11 ` Eric Biggers
2016-10-12 7:16 ` Jan Kara
2016-10-12 17:35 ` Eric Biggers
2016-10-12 21:16 ` Dave Chinner
2016-10-12 21:30 ` Eric Biggers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1474390458.19989.37.camel@redhat.com \
--to=jlayton@redhat.com \
--cc=agruenba@redhat.com \
--cc=akpm@linux-foundation.org \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=viro@ZenIV.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).