Terminal: List all directories for which a user or group has write permission

20,323

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.)

Share:
20,323

Related videos on Youtube

elrobis
Author by

elrobis

GIS Developer Bad hockey player So-so musician Likes fishing

Updated on September 18, 2022

Comments

  • elrobis
    elrobis over 1 year

    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
    
  • elrobis
    elrobis about 8 years
    Hmm.. maybe I'm doing something wrong? But when I execute exactly this to target user ftpgisdata, I'm not getting any results. It just drops down to the next line in the terminal: find -type d -user ftpgisdata -perm /u=w,g=w
  • Oli
    Oli about 8 years
    sudo mkdir cheese; sudo chmod o+w cheese is a directory anybody can write to but won't show in either of these.
  • Oli
    Oli about 8 years
    @elrobis Change them so they start find /. It's currently just searching the current working directory.
  • elrobis
    elrobis about 8 years
    ahh ok. ..so does it follow that if I cd /, that it will search forward from the root and consider any given user or group? It's running right now and looks promising--definite taking a little longer than 2 sec though!
  • muru
    muru about 8 years
    And you'll probably need to run this with sudo to avoid permission errors.
  • elrobis
    elrobis about 8 years
    This seems to work. I ran it on a user I understood and got results I'd expect to get. I'm trying @Oli's approach right now to see how comparable they are.
  • elrobis
    elrobis about 8 years
    I tried @Rinzwind's approach first because this one was a bit scary, however curiously they are getting somewhat different results..!
  • Rinzwind
    Rinzwind about 8 years
    @elrobis Oli's version is a bit more difficult to grasp but also includes ACL ( a tool to set permissions more precise than general chmod can)
  • Oli
    Oli about 8 years
    And simple directories that not owned by the given user that allow others to write.
  • Oli
    Oli about 8 years
    Don't you just need a third option? sudo find / -type d -perm /o=w. Also can't you combine all three searches into one with -o? Still won't do ACL but it's closer.
  • elrobis
    elrobis about 8 years
    Rinzwind and @Oli, I edited to include the output I got from both of your solutions--thoughts? Oli's seems more comprehensive ..and I was particularly intrigued to see that it found a defunct wordpress site lurking on the system. (Exactly the sort of weird stuff I was hoping to learn with this exercise!)
  • Oli
    Oli about 8 years
    @elrobis Try the combined version I edited into this answer. It should also show the Wordpress site and be somewhat faster than my answer.
  • elrobis
    elrobis about 8 years
    The combined result set is pretty noisy. It includes specific files rather than just directories (not necessarily a bad thing), but alot of the results don't make sense for the user I tested, and when I ran ls -la on one of the paths returned, it provided numbers (id's maybe?) rather than seemingly legit user/group names for what struck me as a very vague, low-level path.
  • elrobis
    elrobis about 8 years
    True, I am logged in as route, but basically I want to learn "hey what directories can X user write to?" I just tried your approach on user with id=1001, i.e. $1=='1001', but it didn't return any hits. Thoughts?
  • Sergiy Kolodyazhnyy
    Sergiy Kolodyazhnyy about 8 years
    @elrobis Yeah, I've got side tracked - I realize that you wanted to check write permissions on a directory per user. Thing is , a user may be part of a group, so they can write to directory. For example, directory group is foobar, but user that belongs to that group may have usernamescooby. This is a more complex problem that it seems . . .I'll be working on it more, probably will need to write functions for checking user and checking group write access on a file
  • Peter Cordes
    Peter Cordes about 8 years
    @Oli and Rinzwind: this misses directories with group-ownership of one of the user's secondary groups, doesn't it? e.g. chown root:users /data/share && chmod 2775 /data/share, where some accounts are members of the users group.
  • Oli
    Oli about 8 years
    @PeterCordes Yeah. I'm sure there's a way of wrapping the find command in a function that builds out all the various things to search but by that point, we might all have expired.
  • Peter Cordes
    Peter Cordes about 8 years
    @Oli: Or just use GNU find's -writable, like I did in my answer. It uses access(2) to check writeability.
  • Oli
    Oli about 8 years
    The problem with running the find as non-root is you don't evaluate the entire tree. For example, create the following: sudo mkdir -p cheese/deep; sudo chmod o+w cheese/deep; sudo chmod o-r cheese anybody can write to cheese/deep but they can't read the contents of the parent cheese directory, and therefore find can't see cheese/deep. Your find will just error out (and suppress the error). Sorry.
  • Oli
    Oli about 8 years
    @PeterCordes Unfortunately running find as the user has its own problems. I've commented with a breaking example.
  • Peter Cordes
    Peter Cordes about 8 years
    @Oli: Oh right, I overlooked the x without r case. Thanks. I guess we could construct a find command line with a shell loop that iterates over the user's secondary groups, if we don't want to just pipe root-find output into another process that does access(2) checks as the user. That 2nd process could be find -writable.
  • elrobis
    elrobis about 8 years
    Peter thanks for your contribution and the helpful description. I ran your solution and updated my post to include the result for the same user I've been testing against.
  • Rinzwind
    Rinzwind about 8 years
    Also a nice approach but this turned out to be pretty difficult and you'd almost file for a bug report to improve this D: The question is a valid one when you look at securing a system. And it is just too darn difficult.