aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWang Shilong <wangshilong1991@gmail.com>2018-10-03 10:33:32 -0400
committerTheodore Ts'o <tytso@mit.edu>2018-10-03 10:33:32 -0400
commitdc7ac6c4cae3b58724c2f1e21a7c05ce19ecd5a8 (patch)
tree332457b7a5a12fdc8231bfa3f962ab88dbfaebda
parentc0e3e0406a0c39044c7dc25f3386694542d50fcc (diff)
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr ioctl, and existed permission check inode_owner_or_capable() is obviously not enough, just think that common users could change project id of file, that could make users to break project quota easily. This patch try to follow same regular of xfs project quota: "Project Quota ID state is only allowed to change from within the init namespace. Enforce that restriction only if we are trying to change the quota ID state. Everything else is allowed in user namespaces." Besides that, check and set project id'state should be an atomic operation, protect whole operation with inode lock, ext4_ioctl_setproject() is only used for ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file() before ext4_ioctl_setflags(), and ext4_ioctl_setproject() is called after ext4_ioctl_setflags(), we could share codes, so remove it inside ext4_ioctl_setproject(). Signed-off-by: Wang Shilong <wshilong@ddn.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Reviewed-by: Andreas Dilger <adilger@dilger.ca> Cc: stable@kernel.org
-rw-r--r--fs/ext4/ioctl.c60
1 files changed, 37 insertions, 23 deletions
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index d7ed7487e630..0b3e2486f988 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -360,19 +360,14 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
360 if (projid_eq(kprojid, EXT4_I(inode)->i_projid)) 360 if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
361 return 0; 361 return 0;
362 362
363 err = mnt_want_write_file(filp);
364 if (err)
365 return err;
366
367 err = -EPERM; 363 err = -EPERM;
368 inode_lock(inode);
369 /* Is it quota file? Do not allow user to mess with it */ 364 /* Is it quota file? Do not allow user to mess with it */
370 if (ext4_is_quota_file(inode)) 365 if (ext4_is_quota_file(inode))
371 goto out_unlock; 366 return err;
372 367
373 err = ext4_get_inode_loc(inode, &iloc); 368 err = ext4_get_inode_loc(inode, &iloc);
374 if (err) 369 if (err)
375 goto out_unlock; 370 return err;
376 371
377 raw_inode = ext4_raw_inode(&iloc); 372 raw_inode = ext4_raw_inode(&iloc);
378 if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) { 373 if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) {
@@ -380,7 +375,7 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
380 EXT4_SB(sb)->s_want_extra_isize, 375 EXT4_SB(sb)->s_want_extra_isize,
381 &iloc); 376 &iloc);
382 if (err) 377 if (err)
383 goto out_unlock; 378 return err;
384 } else { 379 } else {
385 brelse(iloc.bh); 380 brelse(iloc.bh);
386 } 381 }
@@ -390,10 +385,8 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
390 handle = ext4_journal_start(inode, EXT4_HT_QUOTA, 385 handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
391 EXT4_QUOTA_INIT_BLOCKS(sb) + 386 EXT4_QUOTA_INIT_BLOCKS(sb) +
392 EXT4_QUOTA_DEL_BLOCKS(sb) + 3); 387 EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
393 if (IS_ERR(handle)) { 388 if (IS_ERR(handle))
394 err = PTR_ERR(handle); 389 return PTR_ERR(handle);
395 goto out_unlock;
396 }
397 390
398 err = ext4_reserve_inode_write(handle, inode, &iloc); 391 err = ext4_reserve_inode_write(handle, inode, &iloc);
399 if (err) 392 if (err)
@@ -421,9 +414,6 @@ out_dirty:
421 err = rc; 414 err = rc;
422out_stop: 415out_stop:
423 ext4_journal_stop(handle); 416 ext4_journal_stop(handle);
424out_unlock:
425 inode_unlock(inode);
426 mnt_drop_write_file(filp);
427 return err; 417 return err;
428} 418}
429#else 419#else
@@ -647,6 +637,30 @@ group_add_out:
647 return err; 637 return err;
648} 638}
649 639
640static int ext4_ioctl_check_project(struct inode *inode, struct fsxattr *fa)
641{
642 /*
643 * Project Quota ID state is only allowed to change from within the init
644 * namespace. Enforce that restriction only if we are trying to change
645 * the quota ID state. Everything else is allowed in user namespaces.
646 */
647 if (current_user_ns() == &init_user_ns)
648 return 0;
649
650 if (__kprojid_val(EXT4_I(inode)->i_projid) != fa->fsx_projid)
651 return -EINVAL;
652
653 if (ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT)) {
654 if (!(fa->fsx_xflags & FS_XFLAG_PROJINHERIT))
655 return -EINVAL;
656 } else {
657 if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
658 return -EINVAL;
659 }
660
661 return 0;
662}
663
650long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 664long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
651{ 665{
652 struct inode *inode = file_inode(filp); 666 struct inode *inode = file_inode(filp);
@@ -1046,19 +1060,19 @@ resizefs_out:
1046 return err; 1060 return err;
1047 1061
1048 inode_lock(inode); 1062 inode_lock(inode);
1063 err = ext4_ioctl_check_project(inode, &fa);
1064 if (err)
1065 goto out;
1049 flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) | 1066 flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) |
1050 (flags & EXT4_FL_XFLAG_VISIBLE); 1067 (flags & EXT4_FL_XFLAG_VISIBLE);
1051 err = ext4_ioctl_setflags(inode, flags); 1068 err = ext4_ioctl_setflags(inode, flags);
1052 inode_unlock(inode);
1053 mnt_drop_write_file(filp);
1054 if (err) 1069 if (err)
1055 return err; 1070 goto out;
1056
1057 err = ext4_ioctl_setproject(filp, fa.fsx_projid); 1071 err = ext4_ioctl_setproject(filp, fa.fsx_projid);
1058 if (err) 1072out:
1059 return err; 1073 inode_unlock(inode);
1060 1074 mnt_drop_write_file(filp);
1061 return 0; 1075 return err;
1062 } 1076 }
1063 case EXT4_IOC_SHUTDOWN: 1077 case EXT4_IOC_SHUTDOWN:
1064 return ext4_shutdown(sb, arg); 1078 return ext4_shutdown(sb, arg);