diff mbox series

btrfs: relax conditions on user subvolume delete

Message ID 20200807215954.697124-1-boris@bur.io (mailing list archive)
State New, archived
Headers show
Series btrfs: relax conditions on user subvolume delete | expand

Commit Message

Boris Burkov Aug. 7, 2020, 9:59 p.m. UTC
With the user_subvol_rm_allowed mount flag set, it is possible for non
root users to delete subvolumes. Currently, the conditions to do so
diverge from the conditions for root. Specifically, there is an
additional check on the ability to write to the subvolume to be deleted,
not just its containing directory. As a result, we see weird behavior
like root being able to delete a read only subvolume it doesn't have
write permissions for, but a user not being able to delete a read only
subvolume it does otherwise have write permissions for.

Furthermore, the existing comments make allusions to rmdir being allowed
as a standard, and rmdir only checks permissions on the containing
directory, as in the root case.

To streamline this, make user subvolume deletes with the mount flag set
match the behavior for root. For increased security, it is simple to
just mount without user_subvol_rm_allowed.

To put it plainly, the risk is that a non-root user who has write access
to a parent directory will be able to delete a subvolume in the parent,
even if they don't have write access to the subvolume. Currently, only
root can do that.

Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/btrfs/ioctl.c | 30 +-----------------------------
 1 file changed, 1 insertion(+), 29 deletions(-)

Comments

kernel test robot Aug. 8, 2020, 12:31 a.m. UTC | #1
Hi Boris,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on kdave/for-next]
[also build test WARNING on v5.8 next-20200807]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Boris-Burkov/btrfs-relax-conditions-on-user-subvolume-delete/20200808-060125
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
config: i386-randconfig-s001-20200807 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce:
        # apt-get install sparse
        # sparse version: v0.6.2-118-ge1578773-dirty
        # save the attached .config to linux build tree
        make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=i386 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from fs/btrfs/ioctl.c:29:
   fs/btrfs/ctree.h:2265:8: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
    2265 | size_t __const btrfs_get_num_csums(void);
         |        ^~~~~~~
   In file included from fs/btrfs/ioctl.c:43:
   fs/btrfs/sysfs.h:16:1: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
      16 | const char * const btrfs_feature_set_name(enum btrfs_feature_set set);
         | ^~~~~
   fs/btrfs/ioctl.c: In function 'btrfs_ioctl_snap_destroy':
>> fs/btrfs/ioctl.c:2838:21: warning: variable 'dest' set but not used [-Wunused-but-set-variable]
    2838 |  struct btrfs_root *dest = NULL;
         |                     ^~~~

vim +/dest +2838 fs/btrfs/ioctl.c

42e4b520c812da Tomohiro Misono       2018-05-21  2828  
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2829  static noinline int btrfs_ioctl_snap_destroy(struct file *file,
949964c928430a Marcos Paulo de Souza 2020-02-07  2830  					     void __user *arg,
949964c928430a Marcos Paulo de Souza 2020-02-07  2831  					     bool destroy_v2)
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2832  {
54563d41a58be7 Al Viro               2013-09-01  2833  	struct dentry *parent = file->f_path.dentry;
0b246afa62b0cf Jeff Mahoney          2016-06-22  2834  	struct btrfs_fs_info *fs_info = btrfs_sb(parent->d_sb);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2835  	struct dentry *dentry;
2b0143b5c986be David Howells         2015-03-17  2836  	struct inode *dir = d_inode(parent);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2837  	struct inode *inode;
76dda93c6ae2c1 Yan, Zheng            2009-09-21 @2838  	struct btrfs_root *dest = NULL;
949964c928430a Marcos Paulo de Souza 2020-02-07  2839  	struct btrfs_ioctl_vol_args *vol_args = NULL;
949964c928430a Marcos Paulo de Souza 2020-02-07  2840  	struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL;
949964c928430a Marcos Paulo de Souza 2020-02-07  2841  	char *subvol_name, *subvol_name_ptr = NULL;
949964c928430a Marcos Paulo de Souza 2020-02-07  2842  	int subvol_namelen;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2843  	int err = 0;
949964c928430a Marcos Paulo de Souza 2020-02-07  2844  	bool destroy_parent = false;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2845  
949964c928430a Marcos Paulo de Souza 2020-02-07  2846  	if (destroy_v2) {
949964c928430a Marcos Paulo de Souza 2020-02-07  2847  		vol_args2 = memdup_user(arg, sizeof(*vol_args2));
949964c928430a Marcos Paulo de Souza 2020-02-07  2848  		if (IS_ERR(vol_args2))
949964c928430a Marcos Paulo de Souza 2020-02-07  2849  			return PTR_ERR(vol_args2);
325c50e3cebb92 Jeff Mahoney          2016-09-21  2850  
949964c928430a Marcos Paulo de Souza 2020-02-07  2851  		if (vol_args2->flags & ~BTRFS_SUBVOL_DELETE_ARGS_MASK) {
949964c928430a Marcos Paulo de Souza 2020-02-07  2852  			err = -EOPNOTSUPP;
949964c928430a Marcos Paulo de Souza 2020-02-07  2853  			goto out;
949964c928430a Marcos Paulo de Souza 2020-02-07  2854  		}
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2855  
949964c928430a Marcos Paulo de Souza 2020-02-07  2856  		/*
949964c928430a Marcos Paulo de Souza 2020-02-07  2857  		 * If SPEC_BY_ID is not set, we are looking for the subvolume by
949964c928430a Marcos Paulo de Souza 2020-02-07  2858  		 * name, same as v1 currently does.
949964c928430a Marcos Paulo de Souza 2020-02-07  2859  		 */
949964c928430a Marcos Paulo de Souza 2020-02-07  2860  		if (!(vol_args2->flags & BTRFS_SUBVOL_SPEC_BY_ID)) {
949964c928430a Marcos Paulo de Souza 2020-02-07  2861  			vol_args2->name[BTRFS_SUBVOL_NAME_MAX] = 0;
949964c928430a Marcos Paulo de Souza 2020-02-07  2862  			subvol_name = vol_args2->name;
949964c928430a Marcos Paulo de Souza 2020-02-07  2863  
949964c928430a Marcos Paulo de Souza 2020-02-07  2864  			err = mnt_want_write_file(file);
949964c928430a Marcos Paulo de Souza 2020-02-07  2865  			if (err)
949964c928430a Marcos Paulo de Souza 2020-02-07  2866  				goto out;
949964c928430a Marcos Paulo de Souza 2020-02-07  2867  		} else {
949964c928430a Marcos Paulo de Souza 2020-02-07  2868  			if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) {
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2869  				err = -EINVAL;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2870  				goto out;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2871  			}
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2872  
a561be7100cd61 Al Viro               2011-11-23  2873  			err = mnt_want_write_file(file);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2874  			if (err)
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2875  				goto out;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2876  
949964c928430a Marcos Paulo de Souza 2020-02-07  2877  			dentry = btrfs_get_dentry(fs_info->sb,
949964c928430a Marcos Paulo de Souza 2020-02-07  2878  					BTRFS_FIRST_FREE_OBJECTID,
949964c928430a Marcos Paulo de Souza 2020-02-07  2879  					vol_args2->subvolid, 0, 0);
949964c928430a Marcos Paulo de Souza 2020-02-07  2880  			if (IS_ERR(dentry)) {
949964c928430a Marcos Paulo de Souza 2020-02-07  2881  				err = PTR_ERR(dentry);
949964c928430a Marcos Paulo de Souza 2020-02-07  2882  				goto out_drop_write;
949964c928430a Marcos Paulo de Souza 2020-02-07  2883  			}
949964c928430a Marcos Paulo de Souza 2020-02-07  2884  
949964c928430a Marcos Paulo de Souza 2020-02-07  2885  			/*
949964c928430a Marcos Paulo de Souza 2020-02-07  2886  			 * Change the default parent since the subvolume being
949964c928430a Marcos Paulo de Souza 2020-02-07  2887  			 * deleted can be outside of the current mount point.
949964c928430a Marcos Paulo de Souza 2020-02-07  2888  			 */
949964c928430a Marcos Paulo de Souza 2020-02-07  2889  			parent = btrfs_get_parent(dentry);
949964c928430a Marcos Paulo de Souza 2020-02-07  2890  
949964c928430a Marcos Paulo de Souza 2020-02-07  2891  			/*
949964c928430a Marcos Paulo de Souza 2020-02-07  2892  			 * At this point dentry->d_name can point to '/' if the
949964c928430a Marcos Paulo de Souza 2020-02-07  2893  			 * subvolume we want to destroy is outsite of the
949964c928430a Marcos Paulo de Souza 2020-02-07  2894  			 * current mount point, so we need to release the
949964c928430a Marcos Paulo de Souza 2020-02-07  2895  			 * current dentry and execute the lookup to return a new
949964c928430a Marcos Paulo de Souza 2020-02-07  2896  			 * one with ->d_name pointing to the
949964c928430a Marcos Paulo de Souza 2020-02-07  2897  			 * <mount point>/subvol_name.
949964c928430a Marcos Paulo de Souza 2020-02-07  2898  			 */
949964c928430a Marcos Paulo de Souza 2020-02-07  2899  			dput(dentry);
949964c928430a Marcos Paulo de Souza 2020-02-07  2900  			if (IS_ERR(parent)) {
949964c928430a Marcos Paulo de Souza 2020-02-07  2901  				err = PTR_ERR(parent);
949964c928430a Marcos Paulo de Souza 2020-02-07  2902  				goto out_drop_write;
949964c928430a Marcos Paulo de Souza 2020-02-07  2903  			}
949964c928430a Marcos Paulo de Souza 2020-02-07  2904  			dir = d_inode(parent);
949964c928430a Marcos Paulo de Souza 2020-02-07  2905  
949964c928430a Marcos Paulo de Souza 2020-02-07  2906  			/*
949964c928430a Marcos Paulo de Souza 2020-02-07  2907  			 * If v2 was used with SPEC_BY_ID, a new parent was
949964c928430a Marcos Paulo de Souza 2020-02-07  2908  			 * allocated since the subvolume can be outside of the
949964c928430a Marcos Paulo de Souza 2020-02-07  2909  			 * current mount point. Later on we need to release this
949964c928430a Marcos Paulo de Souza 2020-02-07  2910  			 * new parent dentry.
949964c928430a Marcos Paulo de Souza 2020-02-07  2911  			 */
949964c928430a Marcos Paulo de Souza 2020-02-07  2912  			destroy_parent = true;
949964c928430a Marcos Paulo de Souza 2020-02-07  2913  
949964c928430a Marcos Paulo de Souza 2020-02-07  2914  			subvol_name_ptr = btrfs_get_subvol_name_from_objectid(
949964c928430a Marcos Paulo de Souza 2020-02-07  2915  						fs_info, vol_args2->subvolid);
949964c928430a Marcos Paulo de Souza 2020-02-07  2916  			if (IS_ERR(subvol_name_ptr)) {
949964c928430a Marcos Paulo de Souza 2020-02-07  2917  				err = PTR_ERR(subvol_name_ptr);
949964c928430a Marcos Paulo de Souza 2020-02-07  2918  				goto free_parent;
949964c928430a Marcos Paulo de Souza 2020-02-07  2919  			}
949964c928430a Marcos Paulo de Souza 2020-02-07  2920  			/* subvol_name_ptr is already NULL termined */
949964c928430a Marcos Paulo de Souza 2020-02-07  2921  			subvol_name = (char *)kbasename(subvol_name_ptr);
949964c928430a Marcos Paulo de Souza 2020-02-07  2922  		}
949964c928430a Marcos Paulo de Souza 2020-02-07  2923  	} else {
949964c928430a Marcos Paulo de Souza 2020-02-07  2924  		vol_args = memdup_user(arg, sizeof(*vol_args));
949964c928430a Marcos Paulo de Souza 2020-02-07  2925  		if (IS_ERR(vol_args))
949964c928430a Marcos Paulo de Souza 2020-02-07  2926  			return PTR_ERR(vol_args);
949964c928430a Marcos Paulo de Souza 2020-02-07  2927  
949964c928430a Marcos Paulo de Souza 2020-02-07  2928  		vol_args->name[BTRFS_PATH_NAME_MAX] = 0;
949964c928430a Marcos Paulo de Souza 2020-02-07  2929  		subvol_name = vol_args->name;
949964c928430a Marcos Paulo de Souza 2020-02-07  2930  
949964c928430a Marcos Paulo de Souza 2020-02-07  2931  		err = mnt_want_write_file(file);
949964c928430a Marcos Paulo de Souza 2020-02-07  2932  		if (err)
949964c928430a Marcos Paulo de Souza 2020-02-07  2933  			goto out;
949964c928430a Marcos Paulo de Souza 2020-02-07  2934  	}
949964c928430a Marcos Paulo de Souza 2020-02-07  2935  
949964c928430a Marcos Paulo de Souza 2020-02-07  2936  	subvol_namelen = strlen(subvol_name);
949964c928430a Marcos Paulo de Souza 2020-02-07  2937  
949964c928430a Marcos Paulo de Souza 2020-02-07  2938  	if (strchr(subvol_name, '/') ||
949964c928430a Marcos Paulo de Souza 2020-02-07  2939  	    strncmp(subvol_name, "..", subvol_namelen) == 0) {
949964c928430a Marcos Paulo de Souza 2020-02-07  2940  		err = -EINVAL;
949964c928430a Marcos Paulo de Souza 2020-02-07  2941  		goto free_subvol_name;
949964c928430a Marcos Paulo de Souza 2020-02-07  2942  	}
949964c928430a Marcos Paulo de Souza 2020-02-07  2943  
949964c928430a Marcos Paulo de Souza 2020-02-07  2944  	if (!S_ISDIR(dir->i_mode)) {
949964c928430a Marcos Paulo de Souza 2020-02-07  2945  		err = -ENOTDIR;
949964c928430a Marcos Paulo de Souza 2020-02-07  2946  		goto free_subvol_name;
949964c928430a Marcos Paulo de Souza 2020-02-07  2947  	}
521e0546c970c3 David Sterba          2014-04-15  2948  
002354112f1e3c Al Viro               2016-05-26  2949  	err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
002354112f1e3c Al Viro               2016-05-26  2950  	if (err == -EINTR)
949964c928430a Marcos Paulo de Souza 2020-02-07  2951  		goto free_subvol_name;
949964c928430a Marcos Paulo de Souza 2020-02-07  2952  	dentry = lookup_one_len(subvol_name, parent, subvol_namelen);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2953  	if (IS_ERR(dentry)) {
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2954  		err = PTR_ERR(dentry);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2955  		goto out_unlock_dir;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2956  	}
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2957  
2b0143b5c986be David Howells         2015-03-17  2958  	if (d_really_is_negative(dentry)) {
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2959  		err = -ENOENT;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2960  		goto out_dput;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2961  	}
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2962  
2b0143b5c986be David Howells         2015-03-17  2963  	inode = d_inode(dentry);
4260f7c7516f4c Sage Weil             2010-10-29  2964  	dest = BTRFS_I(inode)->root;
4260f7c7516f4c Sage Weil             2010-10-29  2965  	if (!capable(CAP_SYS_ADMIN)) {
42e1d11143844c Boris Burkov          2020-08-07  2966  		/* Regular user.  Only allow this with a special mount option */
4260f7c7516f4c Sage Weil             2010-10-29  2967  		err = -EPERM;
0b246afa62b0cf Jeff Mahoney          2016-06-22  2968  		if (!btrfs_test_opt(fs_info, USER_SUBVOL_RM_ALLOWED))
4260f7c7516f4c Sage Weil             2010-10-29  2969  			goto out_dput;
5c39da5b6ca23e Miao Xie              2012-10-22  2970  	}
4260f7c7516f4c Sage Weil             2010-10-29  2971  
5c39da5b6ca23e Miao Xie              2012-10-22  2972  	/* check if subvolume may be deleted by a user */
4260f7c7516f4c Sage Weil             2010-10-29  2973  	err = btrfs_may_delete(dir, dentry, 1);
4260f7c7516f4c Sage Weil             2010-10-29  2974  	if (err)
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2975  		goto out_dput;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2976  
4a0cc7ca6c40b6 Nikolay Borisov       2017-01-10  2977  	if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) {
4260f7c7516f4c Sage Weil             2010-10-29  2978  		err = -EINVAL;
4260f7c7516f4c Sage Weil             2010-10-29  2979  		goto out_dput;
4260f7c7516f4c Sage Weil             2010-10-29  2980  	}
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2981  
5955102c9984fa Al Viro               2016-01-22  2982  	inode_lock(inode);
f60a2364a4eee4 Misono Tomohiro       2018-04-18  2983  	err = btrfs_delete_subvolume(dir, dentry);
5955102c9984fa Al Viro               2016-01-22  2984  	inode_unlock(inode);
46008d9d3f0ef7 Amir Goldstein        2019-05-26  2985  	if (!err) {
46008d9d3f0ef7 Amir Goldstein        2019-05-26  2986  		fsnotify_rmdir(dir, dentry);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2987  		d_delete(dentry);
46008d9d3f0ef7 Amir Goldstein        2019-05-26  2988  	}
fa6ac8765c48a0 Liu Bo                2013-02-20  2989  
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2990  out_dput:
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2991  	dput(dentry);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  2992  out_unlock_dir:
5955102c9984fa Al Viro               2016-01-22  2993  	inode_unlock(dir);
949964c928430a Marcos Paulo de Souza 2020-02-07  2994  free_subvol_name:
949964c928430a Marcos Paulo de Souza 2020-02-07  2995  	kfree(subvol_name_ptr);
949964c928430a Marcos Paulo de Souza 2020-02-07  2996  free_parent:
949964c928430a Marcos Paulo de Souza 2020-02-07  2997  	if (destroy_parent)
949964c928430a Marcos Paulo de Souza 2020-02-07  2998  		dput(parent);
002354112f1e3c Al Viro               2016-05-26  2999  out_drop_write:
2a79f17e4a641a Al Viro               2011-12-09  3000  	mnt_drop_write_file(file);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  3001  out:
949964c928430a Marcos Paulo de Souza 2020-02-07  3002  	kfree(vol_args2);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  3003  	kfree(vol_args);
76dda93c6ae2c1 Yan, Zheng            2009-09-21  3004  	return err;
76dda93c6ae2c1 Yan, Zheng            2009-09-21  3005  }
76dda93c6ae2c1 Yan, Zheng            2009-09-21  3006  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index bd3511c5ca81..475b99241564 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2835,7 +2835,6 @@  static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 	struct dentry *dentry;
 	struct inode *dir = d_inode(parent);
 	struct inode *inode;
-	struct btrfs_root *root = BTRFS_I(dir)->root;
 	struct btrfs_root *dest = NULL;
 	struct btrfs_ioctl_vol_args *vol_args = NULL;
 	struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL;
@@ -2964,37 +2963,10 @@  static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 	inode = d_inode(dentry);
 	dest = BTRFS_I(inode)->root;
 	if (!capable(CAP_SYS_ADMIN)) {
-		/*
-		 * Regular user.  Only allow this with a special mount
-		 * option, when the user has write+exec access to the
-		 * subvol root, and when rmdir(2) would have been
-		 * allowed.
-		 *
-		 * Note that this is _not_ check that the subvol is
-		 * empty or doesn't contain data that we wouldn't
-		 * otherwise be able to delete.
-		 *
-		 * Users who want to delete empty subvols should try
-		 * rmdir(2).
-		 */
+		/* Regular user.  Only allow this with a special mount option */
 		err = -EPERM;
 		if (!btrfs_test_opt(fs_info, USER_SUBVOL_RM_ALLOWED))
 			goto out_dput;
-
-		/*
-		 * Do not allow deletion if the parent dir is the same
-		 * as the dir to be deleted.  That means the ioctl
-		 * must be called on the dentry referencing the root
-		 * of the subvol, not a random directory contained
-		 * within it.
-		 */
-		err = -EINVAL;
-		if (root == dest)
-			goto out_dput;
-
-		err = inode_permission(inode, MAY_WRITE | MAY_EXEC);
-		if (err)
-			goto out_dput;
 	}
 
 	/* check if subvolume may be deleted by a user */