Commit 4260f7c7516f4c209cf0ca34fda99cc9a0847772

Authored by Sage Weil
Committed by Chris Mason
1 parent 531cb13f1e

Btrfs: allow subvol deletion by unprivileged user with -o user_subvol_rm_allowed

Add a mount option user_subvol_rm_allowed that allows users to delete a
(potentially non-empty!) subvol when they would otherwise we allowed to do
an rmdir(2).  We duplicate the may_delete() checks from the core VFS code
to implement identical security checks (minus the directory size check).
We additionally require that the user has write+exec permission on the
subvol root inode.

Signed-off-by: Sage Weil <sage@newdream.net>
Signed-off-by: Chris Mason <chris.mason@oracle.com>

Showing 3 changed files with 116 additions and 5 deletions Side-by-side Diff

... ... @@ -1234,6 +1234,7 @@
1234 1234 #define BTRFS_MOUNT_FORCE_COMPRESS (1 << 11)
1235 1235 #define BTRFS_MOUNT_SPACE_CACHE (1 << 12)
1236 1236 #define BTRFS_MOUNT_CLEAR_CACHE (1 << 13)
  1237 +#define BTRFS_MOUNT_USER_SUBVOL_RM_ALLOWED (1 << 14)
1237 1238  
1238 1239 #define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt)
1239 1240 #define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt)
... ... @@ -409,6 +409,76 @@
409 409 return ret;
410 410 }
411 411  
  412 +/* copy of check_sticky in fs/namei.c()
  413 +* It's inline, so penalty for filesystems that don't use sticky bit is
  414 +* minimal.
  415 +*/
  416 +static inline int btrfs_check_sticky(struct inode *dir, struct inode *inode)
  417 +{
  418 + uid_t fsuid = current_fsuid();
  419 +
  420 + if (!(dir->i_mode & S_ISVTX))
  421 + return 0;
  422 + if (inode->i_uid == fsuid)
  423 + return 0;
  424 + if (dir->i_uid == fsuid)
  425 + return 0;
  426 + return !capable(CAP_FOWNER);
  427 +}
  428 +
  429 +/* copy of may_delete in fs/namei.c()
  430 + * Check whether we can remove a link victim from directory dir, check
  431 + * whether the type of victim is right.
  432 + * 1. We can't do it if dir is read-only (done in permission())
  433 + * 2. We should have write and exec permissions on dir
  434 + * 3. We can't remove anything from append-only dir
  435 + * 4. We can't do anything with immutable dir (done in permission())
  436 + * 5. If the sticky bit on dir is set we should either
  437 + * a. be owner of dir, or
  438 + * b. be owner of victim, or
  439 + * c. have CAP_FOWNER capability
  440 + * 6. If the victim is append-only or immutable we can't do antyhing with
  441 + * links pointing to it.
  442 + * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
  443 + * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
  444 + * 9. We can't remove a root or mountpoint.
  445 + * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
  446 + * nfs_async_unlink().
  447 + */
  448 +
  449 +static int btrfs_may_delete(struct inode *dir,struct dentry *victim,int isdir)
  450 +{
  451 + int error;
  452 +
  453 + if (!victim->d_inode)
  454 + return -ENOENT;
  455 +
  456 + BUG_ON(victim->d_parent->d_inode != dir);
  457 + audit_inode_child(victim, dir);
  458 +
  459 + error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
  460 + if (error)
  461 + return error;
  462 + if (IS_APPEND(dir))
  463 + return -EPERM;
  464 + if (btrfs_check_sticky(dir, victim->d_inode)||
  465 + IS_APPEND(victim->d_inode)||
  466 + IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
  467 + return -EPERM;
  468 + if (isdir) {
  469 + if (!S_ISDIR(victim->d_inode->i_mode))
  470 + return -ENOTDIR;
  471 + if (IS_ROOT(victim))
  472 + return -EBUSY;
  473 + } else if (S_ISDIR(victim->d_inode->i_mode))
  474 + return -EISDIR;
  475 + if (IS_DEADDIR(dir))
  476 + return -ENOENT;
  477 + if (victim->d_flags & DCACHE_NFSFS_RENAMED)
  478 + return -EBUSY;
  479 + return 0;
  480 +}
  481 +
412 482 /* copy of may_create in fs/namei.c() */
413 483 static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
414 484 {
... ... @@ -1274,9 +1344,6 @@
1274 1344 int ret;
1275 1345 int err = 0;
1276 1346  
1277   - if (!capable(CAP_SYS_ADMIN))
1278   - return -EPERM;
1279   -
1280 1347 vol_args = memdup_user(arg, sizeof(*vol_args));
1281 1348 if (IS_ERR(vol_args))
1282 1349 return PTR_ERR(vol_args);
1283 1350  
... ... @@ -1306,12 +1373,50 @@
1306 1373 }
1307 1374  
1308 1375 inode = dentry->d_inode;
  1376 + dest = BTRFS_I(inode)->root;
  1377 + if (!capable(CAP_SYS_ADMIN)){
  1378 + /*
  1379 + * Regular user. Only allow this with a special mount
  1380 + * option, when the user has write+exec access to the
  1381 + * subvol root, and when rmdir(2) would have been
  1382 + * allowed.
  1383 + *
  1384 + * Note that this is _not_ check that the subvol is
  1385 + * empty or doesn't contain data that we wouldn't
  1386 + * otherwise be able to delete.
  1387 + *
  1388 + * Users who want to delete empty subvols should try
  1389 + * rmdir(2).
  1390 + */
  1391 + err = -EPERM;
  1392 + if (!btrfs_test_opt(root, USER_SUBVOL_RM_ALLOWED))
  1393 + goto out_dput;
  1394 +
  1395 + /*
  1396 + * Do not allow deletion if the parent dir is the same
  1397 + * as the dir to be deleted. That means the ioctl
  1398 + * must be called on the dentry referencing the root
  1399 + * of the subvol, not a random directory contained
  1400 + * within it.
  1401 + */
  1402 + err = -EINVAL;
  1403 + if (root == dest)
  1404 + goto out_dput;
  1405 +
  1406 + err = inode_permission(inode, MAY_WRITE | MAY_EXEC);
  1407 + if (err)
  1408 + goto out_dput;
  1409 +
  1410 + /* check if subvolume may be deleted by a non-root user */
  1411 + err = btrfs_may_delete(dir, dentry, 1);
  1412 + if (err)
  1413 + goto out_dput;
  1414 + }
  1415 +
1309 1416 if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) {
1310 1417 err = -EINVAL;
1311 1418 goto out_dput;
1312 1419 }
1313   -
1314   - dest = BTRFS_I(inode)->root;
1315 1420  
1316 1421 mutex_lock(&inode->i_mutex);
1317 1422 err = d_invalidate(dentry);
... ... @@ -71,6 +71,7 @@
71 71 Opt_nossd, Opt_ssd_spread, Opt_thread_pool, Opt_noacl, Opt_compress,
72 72 Opt_compress_force, Opt_notreelog, Opt_ratio, Opt_flushoncommit,
73 73 Opt_discard, Opt_space_cache, Opt_clear_cache, Opt_err,
  74 + Opt_user_subvol_rm_allowed,
74 75 };
75 76  
76 77 static match_table_t tokens = {
... ... @@ -96,6 +97,7 @@
96 97 {Opt_discard, "discard"},
97 98 {Opt_space_cache, "space_cache"},
98 99 {Opt_clear_cache, "clear_cache"},
  100 + {Opt_user_subvol_rm_allowed, "user_subvol_rm_allowed"},
99 101 {Opt_err, NULL},
100 102 };
101 103  
... ... @@ -245,6 +247,9 @@
245 247 case Opt_clear_cache:
246 248 printk(KERN_INFO "btrfs: force clearing of disk cache\n");
247 249 btrfs_set_opt(info->mount_opt, CLEAR_CACHE);
  250 + break;
  251 + case Opt_user_subvol_rm_allowed:
  252 + btrfs_set_opt(info->mount_opt, USER_SUBVOL_RM_ALLOWED);
248 253 break;
249 254 case Opt_err:
250 255 printk(KERN_INFO "btrfs: unrecognized mount option "