Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

filesystem_limit/snapshot_limit is incorrectly enforced against root #10545

Merged
merged 2 commits into from
Jul 12, 2020

Conversation

ahrens
Copy link
Member

@ahrens ahrens commented Jul 9, 2020

Motivation and Context

The filesystem_limit and snapshot_limit properties limit the number of
filesystems or snapshots that can be created below this dataset.
According to the manpage, "The limit is not enforced if the user is
allowed to change the limit." Two types of users are allowed to change
the limit:

  1. Those that have been delegated the filesystem_limit or
    snapshot_limit permission, e.g. with
    zfs allow USER filesystem_limit DATASET. This works properly.

  2. A user with elevated system priviliges (e.g. root). This does not
    work - the root user will incorrectly get an error when trying to create
    a snapshot/filesystem, if it exceeds the _limit property.

The problem is that priv_policy_ns() does not work if the cred_t is
not that of the current process. This happens when
dsl_enforce_ds_ss_limits() is called in syncing context (as part of a
sync task's check func) to determine the permissions of the
corresponding user process.

Description

This commit fixes the issue by passing the task_struct (typedef'ed as
a proc_t) to syncing context, and then using has_capability() to
determine if that process is privileged. Note that we still need to
pass the cred_t to syncing context so that we can check if the user
was delegated this permission with zfs allow.

This problem only impacts Linux. Wrappers are added to FreeBSD but it
continues to use priv_check_cred(), which works on arbitrary cred_t.

There's also some minor code cleanup in policy.c, removing the unused all parameter. I kept that as a separate commit for ease of reviewing, but I'll squash them together if there are no objections.

Closes: #8226

How Has This Been Tested?

manual testing of root and delegated user exceeding the snapshot_limit

ZTS including newly-activated tests.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Performance enhancement (non-breaking change which improves efficiency)
  • Code cleanup (non-breaking change which makes code smaller or more readable)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation (a change to man pages or other documentation)

Checklist:

  • My code follows the ZFS on Linux code style requirements.
  • I have updated the documentation accordingly.
  • I have read the contributing document.
  • I have added tests to cover my changes.
  • I have run the ZFS Test Suite with this change applied.
  • All commit messages are properly formatted and contain Signed-off-by.

The filesystem_limit and snapshot_limit properties limit the number of
filesystems or snapshots that can be created below this dataset.
According to the manpage, "The limit is not enforced if the user is
allowed to change the limit."  Two types of users are allowed to change
the limit:

1. Those that have been delegated the `filesystem_limit` or
`snapshot_limit` permission, e.g. with
`zfs allow USER filesystem_limit DATASET`.  This works properly.

2. A user with elevated system priviliges (e.g. root).  This does not
work - the root user will incorrectly get an error when trying to create
a snapshot/filesystem, if it exceeds the `_limit` property.

The problem is that `priv_policy_ns()` does not work if the `cred_t` is
not that of the current process.  This happens when
`dsl_enforce_ds_ss_limits()` is called in syncing context (as part of a
sync task's check func) to determine the permissions of the
corresponding user process.

This commit fixes the issue by passing the `task_struct` (typedef'ed as
a `proc_t`) to syncing context, and then using `has_capability()` to
determine if that process is privileged.  Note that we still need to
pass the `cred_t` to syncing context so that we can check if the user
was delegated this permission with `zfs allow`.

This problem only impacts Linux.  Wrappers are added to FreeBSD but it
continues to use `priv_check_cred()`, which works on arbitrary `cred_t`.

Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes: openzfs#8226
@ahrens ahrens added the Status: Code Review Needed Ready for review and testing label Jul 9, 2020
@ahrens ahrens requested review from a user and behlendorf July 9, 2020 03:35
@codecov
Copy link

codecov bot commented Jul 9, 2020

Codecov Report

Merging #10545 into master will increase coverage by 0.14%.
The diff coverage is 75.67%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #10545      +/-   ##
==========================================
+ Coverage   79.71%   79.85%   +0.14%     
==========================================
  Files         392      392              
  Lines      124609   124623      +14     
==========================================
+ Hits        99327    99516     +189     
+ Misses      25282    25107     -175     
Flag Coverage Δ
#kernel 80.19% <80.00%> (+0.02%) ⬆️
#user 66.23% <53.33%> (+0.48%) ⬆️
Impacted Files Coverage Δ
include/sys/dsl_dataset.h 100.00% <ø> (ø)
include/sys/dsl_dir.h 100.00% <ø> (ø)
lib/libzpool/kernel.c 64.53% <0.00%> (-0.30%) ⬇️
module/os/linux/zfs/policy.c 55.40% <66.66%> (+1.23%) ⬆️
module/zfs/dmu_objset.c 91.64% <100.00%> (+0.01%) ⬆️
module/zfs/dmu_recv.c 76.39% <100.00%> (+0.31%) ⬆️
module/zfs/dsl_dataset.c 92.56% <100.00%> (+<0.01%) ⬆️
module/zfs/dsl_dir.c 89.09% <100.00%> (+0.54%) ⬆️
module/zfs/zcp.c 89.77% <100.00%> (+0.02%) ⬆️
module/zfs/zcp_synctask.c 90.96% <100.00%> (+0.11%) ⬆️
... and 61 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a4b0a74...21139c1. Read the comment docs.

Copy link
Contributor

@behlendorf behlendorf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for keeping them separate for the review. I'm happy to squash them when merging.

@behlendorf behlendorf added Status: Accepted Ready to integrate (reviewed, tested) and removed Status: Code Review Needed Ready for review and testing labels Jul 10, 2020
@behlendorf behlendorf merged commit e59a377 into openzfs:master Jul 12, 2020
@ahrens ahrens deleted the cred2 branch July 15, 2020 05:01
jsai20 pushed a commit to jsai20/zfs that referenced this pull request Mar 30, 2021
The filesystem_limit and snapshot_limit properties limit the number of
filesystems or snapshots that can be created below this dataset.
According to the manpage, "The limit is not enforced if the user is
allowed to change the limit."  Two types of users are allowed to change
the limit:

1. Those that have been delegated the `filesystem_limit` or
`snapshot_limit` permission, e.g. with
`zfs allow USER filesystem_limit DATASET`.  This works properly.

2. A user with elevated system privileges (e.g. root).  This does not
work - the root user will incorrectly get an error when trying to create
a snapshot/filesystem, if it exceeds the `_limit` property.

The problem is that `priv_policy_ns()` does not work if the `cred_t` is
not that of the current process.  This happens when
`dsl_enforce_ds_ss_limits()` is called in syncing context (as part of a
sync task's check func) to determine the permissions of the
corresponding user process.

This commit fixes the issue by passing the `task_struct` (typedef'ed as
a `proc_t`) to syncing context, and then using `has_capability()` to
determine if that process is privileged.  Note that we still need to
pass the `cred_t` to syncing context so that we can check if the user
was delegated this permission with `zfs allow`.

This problem only impacts Linux.  Wrappers are added to FreeBSD but it
continues to use `priv_check_cred()`, which works on arbitrary `cred_t`.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Ryan Moeller <ryan@ixsystems.com>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes openzfs#8226
Closes openzfs#10545
sempervictus pushed a commit to sempervictus/zfs that referenced this pull request May 31, 2021
The filesystem_limit and snapshot_limit properties limit the number of
filesystems or snapshots that can be created below this dataset.
According to the manpage, "The limit is not enforced if the user is
allowed to change the limit."  Two types of users are allowed to change
the limit:

1. Those that have been delegated the `filesystem_limit` or
`snapshot_limit` permission, e.g. with
`zfs allow USER filesystem_limit DATASET`.  This works properly.

2. A user with elevated system privileges (e.g. root).  This does not
work - the root user will incorrectly get an error when trying to create
a snapshot/filesystem, if it exceeds the `_limit` property.

The problem is that `priv_policy_ns()` does not work if the `cred_t` is
not that of the current process.  This happens when
`dsl_enforce_ds_ss_limits()` is called in syncing context (as part of a
sync task's check func) to determine the permissions of the
corresponding user process.

This commit fixes the issue by passing the `task_struct` (typedef'ed as
a `proc_t`) to syncing context, and then using `has_capability()` to
determine if that process is privileged.  Note that we still need to
pass the `cred_t` to syncing context so that we can check if the user
was delegated this permission with `zfs allow`.

This problem only impacts Linux.  Wrappers are added to FreeBSD but it
continues to use `priv_check_cred()`, which works on arbitrary `cred_t`.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Ryan Moeller <ryan@ixsystems.com>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes openzfs#8226
Closes openzfs#10545
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Accepted Ready to integrate (reviewed, tested)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Property filesystem_limit does not work as documented
2 participants