104

*nix user permissions are really simple, but things can get messy when you have to take in account all the parent directory access before reaching a given file. How can I check if the user has enough privileges? If not, then which directory is denying access?

For example, suppose a user joe, and the file /long/path/to/file.txt. Even if file.txt was chmoded to 777, joe still has to be able to access /long/, and then /long/path/ and then /long/path/to/ before. What I need is a way to automatically check this. If joe does not have access, I would also like to know where he has been denied. Maybe he can access /long/, but not /long/path/.

slm
  • 363,520
  • 117
  • 767
  • 871
Metalcoder
  • 1,185
  • 2
  • 8
  • 11

5 Answers5

106

To verify access visually, you can use

namei -m /path/to/really/long/directory/with/file/in

which will output all of the permissions in the path in a vertical list.

or

namei -l /path/to/really/long/directory/with/file/in

to list all owners and the permissions. Other answers explain how to verify this programmatically.

Kevin Burke
  • 1,891
  • 4
  • 21
  • 29
exussum
  • 3,973
  • 5
  • 19
  • 21
21

If you have root access, impersonate the user, then run test -r (read), test -w (write), or test -x (execute) to check whether the user can read/write/execute the given file.

sudo -u otheruser test -w /file/to/test || {
   echo "otheruser cannot write the file"
}
Jesse Nickles
  • 165
  • 1
  • 11
Kevin Burke
  • 1,891
  • 4
  • 21
  • 29
  • 2
    This is exact answer what I was searching for. Title of this question could be misleading. – Mojtaba Rezaeian Apr 29 '20 at 05:01
  • 3
    a slight embellishment to check if a process running as "USER:GROUP" can access a given file would be the following command: sudo su - USER -g GROUP -s /bin/bash -c "test -r /path/to/file" – MNB Jan 23 '21 at 14:53
  • What is that `exit 1` doing there, why is it necessary? I am asking just so I learn... – pgr Nov 11 '21 at 15:01
21

You can use bash to do this.

$ cat check-permissions.sh
#!/bin/bash
file=$1
# Handle non-absolute paths
if ! [[ "$file" == /* ]] ; then
    path=.
fi
dirname "$file" | tr '/' $'\n' | while read part ; do
    path="$path/$part"
    # Check for execute permissions
    if ! [[ -x "$path" ]] ; then
        echo "'$path' is blocking access."
    fi
done
if ! [[ -r "$file" ]] ; then
    echo "'$file' is not readable."
fi
$ ./check-permissions.sh /long/path/to/file.txt

To check this for a specific user, you can use sudo.

sudo -u joe ./check-permissions.sh /long/path/to/file.txt
  • sudo -u joe script . Here script is the name of the script file right? so your telling sudo to act like joe was calling the script? – tgkprog Jul 09 '13 at 14:24
  • Precisely. I have modified my answer to clarify that. –  Jul 09 '13 at 14:31
  • I made a slight modification to my script to handle non-absolute paths. –  Jul 09 '13 at 14:47
  • @EvanTeitelman With an absolute path, did you mean to initialize `path` to be empty? or `/`? – Gilles 'SO- stop being evil' Jul 09 '13 at 20:31
  • @Gilles: I meant for it to be empty. In the example, `path` is set to `/long` the first time around the loop, which is correct. Should I set `path` to nothing explicitly (`path=`)? Also, thanks for simplifying out my use of `tr`. –  Jul 09 '13 at 20:58
  • @EvanTeitelman I tried to test it using the following file: /root/test/test.txt (permissions are `0755`, `0700`, and `0777`). I issued `sudo -u joe ./check-permissions.sh /root/test/test.txt`, and nothing happened. Did I missed something? – Metalcoder Jul 10 '13 at 17:45
  • @Metalcoder: An [edit last night](http://unix.stackexchange.com/revisions/82349/5) broke it. I've reverted the changes. –  Jul 10 '13 at 18:48
  • btw, if file had permissions like 000, the script wouldn't tell that it is not readable. – rush Jul 10 '13 at 19:32
  • @rush: I have added that functionality to the script. –  Jul 10 '13 at 19:40
  • I wonder whether it is possible to do something like that without being able to log in as `joe`. It should be possible since `groups joe` returns all groups the user belongs to. – Ben Usman Apr 30 '18 at 18:15
  • UPVOTE/ACCEPT, my target system doesn't have namei. Also if you check, you usually want to react. I can insert setfacl/chmod and add X to user, while checking access in one step – papo Nov 15 '18 at 04:59
5

As I got from your question, you should check it for different users (not only joe), so in that case the easiest way is to recursivly check it via sudo like this:

FILE=$1 ; T_USER=$2 ;
if sudo -u $T_USER [ -r "$FILE" ] ; then
    echo "original file $1 is readable for $T_USER"
else
    while sudo -u $T_USER [ ! -x "$FILE" ] ; do FILE=$(dirname "$FILE") ; done
    echo "only $FILE is readable for $T_USER"
fi

usage:

./script.sh /long/path/to/file.txt joe
rush
  • 27,055
  • 7
  • 87
  • 112
  • Joe needs execute permissions on the directories, not read permissions. –  Jul 09 '13 at 14:20
  • @EvanTeitelman yes, you're right. Fixed. – rush Jul 09 '13 at 14:29
  • @rush I tried to test it using the following file: /root/test/test.txt (permissions are `0755`, `0700`, and `0777`). I issued `./script.sh /root/test/test.txt joe` and it echoed `original file /root/test/test.txt is readable for joe`. Also, while trying this I misstyped the test dir: `./script.sh /root/tst/test.txt joe`, and it echoed `original file /root/tst/test.txt is readable for joe`. Did I missed something? – Metalcoder Jul 10 '13 at 17:46
  • @Metalcoder sorry, it's my fault. There was one extra exclamation. It's removed now, you can try it one more time, it should work fine now. – rush Jul 10 '13 at 17:50
  • @rush it worked! That extra exclamation negates the result of `-r $FILE`, right? – Metalcoder Jul 10 '13 at 18:10
  • @Metalcoder, yes, that's right. – rush Jul 10 '13 at 18:31
2

Here's my attempt at providing this functionality. I've opted to use stat, a while loop, and dirname.

I've created this script, walkdir.bash:

#/bin/bash

cwd="$1"
while [ "x$cwd" != x/ ]; do
  info=`stat "$cwd" |grep "Access: ("`
  printf "%s : %s\n" "$info" "$cwd"

  cwd=`dirname "$cwd"`;
done

You run it like so:

$ walkdir.bash "/home/saml/blog/vmware_networking_tutorial/url.txt"
Access: (0664/-rw-rw-r--)  Uid: (  500/    saml)   Gid: (  501/    saml) : /home/saml/blog/vmware_networking_tutorial/url.txt
Access: (0775/drwxrwxr-x)  Uid: (  500/    saml)   Gid: (  501/    saml) : /home/saml/blog/vmware_networking_tutorial
Access: (0775/drwxrwxr-x)  Uid: (  500/    saml)   Gid: (  501/    saml) : /home/saml/blog
Access: (0700/drwx------)  Uid: (  500/    saml)   Gid: (  501/    saml) : /home/saml
Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root) : /home
slm
  • 363,520
  • 117
  • 767
  • 871