User permissions checking
Background
I’ve currently been working on restricting user access to executables on a Linux box. I removed all executable rights for others and added them via access control lists for certain groups. So for example for cat it looked like this:
# getfacl /usr/bin/cat
getfacl: Removing leading ‘/’ from absolute path names
# file: usr/bin/cat
# owner: root
# group: root
user::rwx
group::r-x
other::r–
For an executable I want the user access to the permissions looked like this:
# getfacl /usr/bin/ping
getfacl: Removing leading ‘/’ from absolute path names
# file: usr/bin/ping
# owner: root
# group: root
user::rwx
group::r-x
group:staff:–x
mask::r-x
other::r–
The Test
As an automated test, I thought I go over all commands and produce a whitelist of executables a given user has access to.
The script looks a bit like this for a single executable:
# cat /tmp/foo.py
import os
the users group id
os.setgid(2000) # the users ID os.setuid(2003)
print(os.access(‘/usr/bin/cat’, os.X_OK)) print(os.access(‘/usr/bin/ping’, os.X_OK))
When I run it I expected the test for the first executable to be false and the second to be true:
# python /tmp/foo.py
True
True
Aeh what? Doesn’t the script run as my user who’s in the staff group?
Turns out there is more to the process than just the group and user id. There are also supplementary groups and capabilities. When changing the script to call print(os.getgroups())
it printed the supplementary groups of the user I was running the script as, which was root in this circumstance. Changing the script to also set the supplementary groups to the one of the user:
import os
the users group id
os.setgid(2000) os.setgroups([2000, 2003]) # the users ID os.setuid(2003)
print(os.access(‘/usr/bin/cat’, os.X_OK)) print(os.access(‘/usr/bin/ping’, os.X_OK))
and running it returns the right results:
# python /tmp/foo.py
False
True
Caveats
Restricting permissions with ACLs and testing the way I demonstrated above can lead to false positives for scripts. You can not remove executable permissions from the script interpreter (e.g. /usr/bin/python
) while keeping it with an ACL on the actual script. The test above will tell you it’s all fine and dandy, while in reality the user will run into a permission denied.