Terminal: List all directories for which a user or group has write permission
I'd like to list all the directories for which a user or group has write permissions.
I found this question on ServerFault, but it addresses a Windows Server, so I'm optimistic there's something better for us in the Linux community. At the same time, I realize there is a recursive twist to this question that may render it impossible without a long-running script.
I'm aware of the following useful commands:
- List all the groups:
cut -d : -f 1 /etc/group
- List all the users:
cut -d : -f 1 /etc/passwd
- Get a user's home dir:
getent passwd user-name| cut -d: -f 6
- This script that lists each user and their group assignments
- Get permissions and user/group details on folder contents:
ls -la
However when I don't have a clue what a user's purpose is, it'd be nice to pull up a list of ALL the directories they have write permission for and start looking around from there. If nothing else it's a super-useful audit..
If it helps to understand my purpose, I inherited (or am at least baby-sitting?) a couple of systems after our SysAdmin took a new position. So I've been trying to understand the evolution of these two systems, such as what software is installed, and where (..ugh), where various config files live, and now, what different groups and users have been created--including the directories they are allowed to write to. Usually I can find such useful terminal commands here on askubuntu, but not finding this one I thought I'd go ahead and ask.
The exact system is Ubuntu 10.04.2, but we also support Ubuntu 12.04.5, so the most version-agnostic solution would be best. Thanks in advance for any help.
[Update: Initial results for the two quick answers]
It's worth noting I'm logged in as root for these, and /
was my working directory. Also, they took a comparable amount of time to run.
@Rinzwind's combined command got the following output in about 5.5 min..
root@tatooine:/# sudo find -type d \( \( -user ftpgisdata -perm /u=w \) -o \( -group ftpgisdata -perm /g=w \) -o -perm /o=w \)
./tmp
./tmp/.ICE-unix
./tmp/.X11-unix
find: `./proc/6594/task/6594/fd/5': No such file or directory
find: `./proc/6594/task/6594/fdinfo/5': No such file or directory
find: `./proc/6594/fd/5': No such file or directory
find: `./proc/6594/fdinfo/5': No such file or directory
./var/tmp
./var/lib/php5
./var/crash
./var/lock
./home/ftpgisdata
./home/ftpgisdata/.ssh
./home/ftpgisdata/.cache
./home/sitename-i-changed.com/wp-content/profile-pics
./dev/shm
@Oli's revised command gets something very similar, also in about 5.5 minutes..
root@tatooine:/# sudo find / -type d -print0 | sudo -u ftpgisdata xargs -0 sh -c 'for p; do [ -w "$p" ] && echo "$p"; done' -
/tmp
/tmp/.ICE-unix
/tmp/.X11-unix
find: `/proc/15541': No such file or directory
find: `/proc/15542': No such file or directory
find: `/proc/15543': No such file or directory
find: `/proc/15567': No such file or directory
find: `/proc/15568/task/15568/fd/5': No such file or directory
find: `/proc/15568/task/15568/fdinfo/5': No such file or directory
find: `/proc/15568/fd/5': No such file or directory
find: `/proc/15568/fdinfo/5': No such file or directory
/var/tmp
/var/lib/php5
/var/crash
/var/lock
/home/ftpgisdata
/home/ftpgisdata/.ssh
/home/ftpgisdata/.cache
/home/sitename-i-changed.com/wp-content/profile-pics
/dev/shm
@PeterCordes answer also returned similar results in about 5.5 minutes..
root@tatooine:~# username_to_check=ftpgisdata base_dir=/ # for example
root@tatooine:~# sudo -u "$username_to_check" find "$base_dir" -type d -writable 2>/dev/null ## GNU find, not POSIX
/tmp
/tmp/.ICE-unix
/tmp/.X11-unix
/proc/7159/task/7159/fd
/proc/7159/fd
/proc/7159/map_files
/var/tmp
/var/lib/php5
/var/crash
/var/lock
/home/ftpgisdata
/home/ftpgisdata/.ssh
/home/ftpgisdata/.cache
/home/sitename-i-changed.com/wp-content/profile-pics
/dev/shm
Solution 1:
Working out what a user can do is hard if you're not that user. You can test various things (is owner, same group, etc) but ACL might apply, there might be no permissions in the mount, who knows. It's hard.
If you can turn into that user, you can test -w <path>
to see if they can write. This isn't as fast as just looking at the inode but it's possible with sudo
.
sudo -u oli test -w <path> && echo "HOORAY"
We can then squirl that onto the back-end of find
. Instead of just using -exec
to change to the oli user over and over and over again (see past revisions), we pipe everything into a xargs instance running as oli. This is much faster.
sudo find / -type d -print0 | sudo -u oli xargs -0 -I{} sh -c 'test -w "$0" && echo "$0"' {}
A somewhat optimised (but visually flabbier) version of this involves minimising the amount of subshelling the xargs performs by piping a stream of paths into a low number of bash subshells. This is undoubtedly faster for big searches.
sudo find / -type d -print0 | sudo -u oli xargs -0 sh -c 'for p; do [ -w "$p" ] && echo "$p"; done' -
Solution 2:
In chat we came to the following instructions for user and group:
sudo find / -type d -user rinzwind -perm /u=w
sudo find / -type d -group rinzwind -perm /g=w
sudo find / -type d -perm /o=w
Or combine all three into one:
sudo find -type d \( \( -user rinzwind -perm /u=w \) -o \( -group rinzwind -perm /g=w \) -o -perm /o=w \)
Without the / it searches current directory.
Took less than 2 seconds on 67Gb of data on my system ;)
Solution 3:
Answer under construction, please be patient
Testing for oneself
One can test write permissions for him/herself with the following code:
[ -w /home/$USER ] && echo yes # using home directory as example
Using -d
flag for test , we can test if something is a directory.
Knowing all that and find
we can do
find /home/ -print0 2> /dev/null | while IFS="" read -r -d "" file ; do [ -d "$file" ] && [ -w "$file" ] && echo "$file" is writeable ; done
Side note:
Obviously , if you get permission errors with find, you cannot read that file, hence there's no reason to output those errors, hence redirection to dev null. Obviously this won't work if we want to find out permissions for users other than oneself, and note - /home is just an example here. Such folders as /var
do have folders shared between multiple users
Testing for others
One could use stat
per each file the find
finds and filter it out with awk
find /var -type d -exec stat --format '%g %n' {} \; 2> /dev/null | awk '$1=='1000'{print}'
Here i am filtering for numeric ID of my own user , 1000, but it could be ID of any user. Of course one can play with options, use group name instead of numeric ids. It's something that is very pliable and adaptable to the purpose you need
Small adjustments
So I've noticed that you mention being logged in as root. Here's an output of my command, where I stat files and print their group and file name, then filter out using AWK by appropriate group name.
$ find /proc -type d -exec stat --format '%G %n' {} \; 2> /dev/null | awk '$1=="syslog"{print}'
syslog /proc/560
syslog /proc/560/task
syslog /proc/560/task/560
syslog /proc/560/task/560/net
syslog /proc/560/task/560/attr
syslog /proc/560/task/562
syslog /proc/560/task/562/net
syslog /proc/560/task/562/attr
syslog /proc/560/task/563
syslog /proc/560/task/563/net
syslog /proc/560/task/563/attr
syslog /proc/560/task/564
syslog /proc/560/task/564/net
syslog /proc/560/task/564/attr
syslog /proc/560/net
syslog /proc/560/attr
^C
Solution 4:
If you have GNU find, you can use the -writable
test (note the spelling, it's not writeable
). That uses the access(2)
system call instead of just trying to figure things out from looking at the permissions, so it works correctly with ACLs. (But might break on NFS with ID mappings).
It will also find directories that are writeable by the user thanks to being a member of a secondary group. (e.g. a directory in /usr/local
writeable by users in the admin
group, or something like chown root:users /data/share && chmod 2775 /data/share
, where some accounts are members of the users
group.)
username_to_check=peter base_dir=/ # for example
sudo -u "$username_to_check" find "$base_dir" -type d -writable 2>/dev/null ## GNU find, not POSIX
There will be errors when find
encounters directories the user can't descend into, so we redirect errors to /dev/null
.
This will not find any directories that are writeable but inside directories without execute permission, because find
running as the user can't traverse the higher directories. However, if a process they own is started inside such a directory, they will be able to write to it with relative paths. (or they get hold of an open file descriptor to such a directory, they can use openat(2)
). pwd
won't work if they don't have exec permission on one of the directory components of the full path, so it's not a common setup.
This also misses writeable directories that are inside executable but not readable directories. To work around those limitations, probably run find -type d
as root, and use find -writable
as the user to check the resulting paths. That might be more efficient than a shell loop using [ -w "$f" ]
.
POSIX find(1)
has far fewer options than GNU find. If your script has to be portable to POSIX, it's probably easiest to pipe into something else that checks permissions, like a shell with [ -w "$f" ]
(see the other answer that suggests this.)