Re: fs: An unexpected ACL pass
From: Eric W. Biederman
Date: Wed Jul 27 2022 - 11:21:53 EST
Adding fsdevel where some of the people involved in the implementation
might see the question.
wangboshi <wangboshi@xxxxxxxxxx> writes:
> Hi, everyone.
>
> We want to talk about a detail about ACL. In `acl_permission_check` function, when file modes don't contain any group permissions, the ACL check is bypassed.
> ```
> static int acl_permission_check(struct user_namespace *mnt_userns,
> struct inode *inode, int mask)
> {
> unsigned int mode = inode->i_mode;
> kuid_t i_uid;
>
> /* Are we the owner? If so, ACL's don't matter */
> i_uid = i_uid_into_mnt(mnt_userns, inode);
> if (likely(uid_eq(current_fsuid(), i_uid))) {
> mask &= 7;
> mode >>= 6;
> return (mask & ~mode) ? -EACCES : 0;
> }
>
> /* Do we have ACL's? */
> if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
> int error = check_acl(mnt_userns, inode, mask);
> if (error != -EAGAIN)
> return error;
> }
>
> /* Only RWX matters for group/other mode bits */
> mask &= 7;
>
> /*
> * Are the group permissions different from
> * the other permissions in the bits we care
> * about? Need to check group ownership if so.
> */
> if (mask & (mode ^ (mode >> 3))) {
> kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
> if (in_group_p(kgid))
> mode >>= 3;
> }
>
> /* Bits in 'mode' clear that we require? */
> return (mask & ~mode) ? -EACCES : 0;
> }
> ```
> It causes that users or groups can get permissions by the other-permission bits even if ACL explicitly restricts that they have no permission.
> For example, we(1000) create a file and set its ACLs which give owner, user(2000), group(3000) and other all permissions with no mask permissions.
> ```
> $ echo data > test
> $ setfacl -m u::rwx,g::rwx,u:2000:rwx,g:3000:rwx,m::-,o:rwx test
> $ getfacl test
> # file: test
> # owner: 1000
> # group: 1000
> user::rwx
> user:2000:rwx #effective:---
> group::rwx #effective:---
> group:3000:rwx #effective:---
> mask::---
> other::rwx
> ```
> Let user(2000) and group(3000) access the file.
> ```
> $ sudo capsh --gid=2000 --uid=2000 -- -c 'cat test'
> data
> $ sudo capsh --gid=3000 --uid=3000 -- -c 'cat test'
> data
> ```
> We can see these successful accesses. It is unexpected. In contrast, according to the ACL access check algorithm described in POSIX 1003.1e draft 17 23.1.5 section, accesses should be denied, because user(2000) and group(3000) are explicitly specified by us and we restrict the permission of the specific users and groups via no mask permissions. And the getfacl tool tells us that all effective permissions of user(1000), group(3000) and owner group contain nothing too.
>
> What's more, if we add x permission to mask permissions, read accesses are denied. It's counterintuitive that we can do something with no permission and are denied with more other permissions.
>
> We want to trace the original of the design. We find similar implementations in earlier version where ext2 ACL was introduced. The design looks like an optimization, but it should base on an assumption that there is no other-permissions if no group-permissions. Is the assumption always valid?
Eric