mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02: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
					
				
					 16 changed files with 89 additions and 102 deletions
				
			
		
							
								
								
									
										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) {
 | 
			
		||||
	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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -626,6 +626,37 @@ posix_acl_create(struct inode *dir, umode_t *mode,
 | 
			
		|||
}
 | 
			
		||||
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.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 *);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue