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

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