Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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
share|improve this question
up vote 6 down vote accepted

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' -
share|improve this answer
    
I tried @Rinzwind's approach first because this one was a bit scary, however curiously they are getting somewhat different results..! – elrobis Mar 16 at 19:18

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

share|improve this answer
    
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 – elrobis Mar 16 at 19:03
    
sudo mkdir cheese; sudo chmod o+w cheese is a directory anybody can write to but won't show in either of these. – Oli Mar 16 at 19:04
    
@elrobis Change them so they start find /. It's currently just searching the current working directory. – Oli Mar 16 at 19:05
    
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! – elrobis Mar 16 at 19:08
    
And you'll probably need to run this with sudo to avoid permission errors. – muru Mar 16 at 19:09

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
share|improve this answer
    
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? – elrobis Mar 16 at 20:00
1  
@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 – Serg Mar 16 at 20:09

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|improve this answer
    
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 Mar 17 at 13:09
    
@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. – Peter Cordes Mar 17 at 13:24
    
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. – elrobis Mar 17 at 13:48
1  
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. – Rinzwind Mar 17 at 14:11

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.