Background
I am trying to wrap my head around how html is unable to access a file via a symlink. I performed the following simple experiment to try and gain an understanding of how apache would be trying to stat/access a file pointed to via a simlink but I unable to understand why it didn't work.
As the user jzhu, I have a symlink in my home directory to a directory in root called other which has the file anaconda-ks.cfg which I want to be able to stat/read in vi. However, I am getting the following results when I am running the stat command.
[jzhu@localhost ~]$ stat /home/jzhu/other/anaconda-ks.cfg
stat: cannot stat `/home/jzhu/other/anaconda-ks.cfg': Permission denied
Here is the stat command of the /home/jzhu/other symlink so you can have further clarification on how I am set up.
[jzhu@localhost ~]$ stat /home/jzhu/other
File: `/home/jzhu/other' -> `/root/other/'
Size: 12 Blocks: 0 IO Block: 4096 symbolic link
Device: fd00h/64768d Inode: 43313 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2013-11-03 08:52:20.602126433 +0000
Modify: 2013-11-03 08:52:20.187918394 +0000
Change: 2013-11-03 08:52:20.187918394 +0000
Looking up the man page for lstat I see the following quote:
These functions return information about a file. No permissions are
required on the file itself, but
— in the case of stat() and lstat() — execute (search) permission is required on all of the directo-
ries in path that lead to the file.
From my understand the stat command does not need executable permissions on /root itself because, unlike lstat, the root directory that it starts off with is the current one in which it is run which means it won't use the full path of "/root/other/" but will use the path "/home/jzhu/other/". I confirmed this via strace with the following relevant output below
lstat("/home/jzhu/other/anaconda-ks.cfg", 0x7fff14d5a7c0) = -1 EACCES (Permission denied)
So in order for me to stat this file, all I should need is executable permission on /home /home/jzhu and /home/jzhu/other
As jzhu, I ran a stat on both /home and /home/jzhu to confirm that I had executable permissions. Shown below
[jzhu@localhost ~]$ stat /home/
File: `/home/'
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fd00h/64768d Inode: 2278 Links: 4
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2013-11-03 08:52:13.797724760 +0000
Modify: 2013-11-04 00:03:45.377150769 +0000
Change: 2013-11-04 00:03:45.377150769 +0000
[jzhu@localhost ~]$ stat /home/jzhu
File: `/home/jzhu'
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fd00h/64768d Inode: 43317 Links: 3
Access: (0700/drwx------) Uid: ( 500/ jzhu) Gid: ( 500/ jzhu)
Access: 2013-11-04 00:00:30.809916040 +0000
Modify: 2013-11-03 23:49:54.781060426 +0000
Change: 2013-11-03 23:49:54.781060426 +0000
However I was unable to stat /home/jzhu/other as jzhu:
[jzhu@localhost ~]$ stat /home/jzhu/other/
stat: cannot stat `/home/jzhu/other/': Permission denied
Changing back too and running stat on the directory. I am confused as to why I am unable to stat the directory as jzhu. As can be seen below, other has read and executable permissions on that directory.
[root@localhost jzhu]# stat /home/jzhu/other/
File: `/home/jzhu/other/'
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fd00h/64768d Inode: 32674 Links: 2
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2013-11-03 08:50:36.935318703 +0000
Modify: 2013-11-03 08:50:35.772737444 +0000
Change: 2013-11-03 08:50:35.772737444 +0000
[root@localhost jzhu]# stat /home/jzhu/other/anaconda-ks.cfg
File: `/home/jzhu/other/anaconda-ks.cfg'
Size: 985 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 43297 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2013-11-03 08:50:35.772737444 +0000
Modify: 2013-11-03 08:50:35.781741701 +0000
Change: 2013-11-03 08:54:50.814196796 +0000
So the two questions I want to ask are the following.
- Why am I unable to stat /home/jzhu/other/ even though I have read and executable permission on other in that directory?
- Why am I unable to stat the anaconda-ks.cfg file?
Best Answer
Your answer lies in the first output you provided:
This shows that
/home/jzhu/other
is a symlink to/root/other
.So basically to access anything in
/home/jzhu/other/
you are going to need access to/root/other
. This means that your user must have execute permissions on both/root
and/root/other
.Next lets go on to this command:
The reason why this fails and the one above works is because of the trailing
/
. On any command which works with a path, if the final component of that path (eg:other
) is a symlink, and the path ends with a trailing/
, then any system calls to operate on that path will try to dereference the symlink, and operate on what the symlink points to instead of the symlink itself.Solutions:
There are 2 possible solutions to this.
1. Change permissions on
/root/other
:As mentioned, add execute capability to both
/root
and/root/other
. You can do this with basic or extended filesystem attributes.You can add the
jzhu
user to theroot
group (usermod -a -G root jzhu
) and add group execute to the path (chmod g+x /root; chmod g+x /root/other
).This isn't an ideal solution as it grants your access to anything restricted to the
root
group.Use filesystem ACLs.
This makes it so that your specific user gets execute access to
/root/other
and anything inside it.It's still not ideal as if these resources are shared, they shouldn't be in root's home directory.
Note that in both these solutions we only grant the execute (
x
) bit. The execute bit is needed on a directory for a user to access anything inside that directory. However you also need the read (r
) bit to be able to list (ls
) the directory. Meaning that without the read bit, you'll have to know exactly where in/root/other
the files are. If you want to allow read, just change thex
in the last 2setfacl
commands torx
(for example,-m u:jzhu:rx
).2. Move the resources outside
/root/other
.This is the preferred solution. You can either get rid of the symlink at
/home/jzhu/other
and create a directory in it's place, or put them in a shared location somewhere else on the system (without knowing what it is, I can't recommend a good location though).The reason this is the preferred solution is that if these resources are shared among users, then they don't belong to a specific user and shouldn't be in that user's home directory.