Bug 1034239 - root cannot deference symbolic links owned by another user
Summary: root cannot deference symbolic links owned by another user
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Fedora
Classification: Fedora
Component: kernel
Version: 19
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: Kernel Maintainer List
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2013-11-25 13:24 UTC by Andrew J. Schorr
Modified: 2013-11-25 16:32 UTC (History)
7 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2013-11-25 16:21:05 UTC
Type: Bug


Attachments (Terms of Use)

Description Andrew J. Schorr 2013-11-25 13:24:16 UTC
Description of problem:
In kernel versions ranging from 3.10.11 through 3.11.8, I have observed that the root superuser is unable to dereference symbolic links owned by another user.  This makes no sense to me.

Version-Release number of selected component (if applicable):
kernel-3.11.8-200.fc19.x86_64


How reproducible:
As a regular user, create a symbolic link.  Note that you can run 'stat -L' without any problem.  But root cannot dereference the link.  The call to stat returns EACCES (Permission denied)

Steps to Reproduce:
1. As a regular (non-root) user: ln -s foo bar
2. As the same user: stat -L bar (should say "No such file or directory" if foo does not exist).
3. As root: stat -L bar

Actual results:
Permission denied

Expected results:
No such file or directory

Additional info:
If the ownership of the symlink is changed to root with "chown -h root:root bar", then everything works as expected.

Comment 1 Josh Boyer 2013-11-25 13:54:42 UTC
This is working fine here.  I tried it on tmpfs and on an ext4 filesystem, with both existing and non-existing files for the link:

[jwboyer@zod kernel]$ cd /tmp
[jwboyer@zod tmp]$ mkdir testdir
[jwboyer@zod tmp]$ cd testdir
[jwboyer@zod testdir]$ pwd
/tmp/testdir
[jwboyer@zod testdir]$ touch bar foo baz biz
[jwboyer@zod testdir]$ ln -s bar foobar
[jwboyer@zod testdir]$ ls -l
total 0
-rw-rw-r--. 1 jwboyer jwboyer 0 Nov 25 08:48 bar
-rw-rw-r--. 1 jwboyer jwboyer 0 Nov 25 08:48 baz
-rw-rw-r--. 1 jwboyer jwboyer 0 Nov 25 08:48 biz
-rw-rw-r--. 1 jwboyer jwboyer 0 Nov 25 08:48 foo
lrwxrwxrwx. 1 jwboyer jwboyer 3 Nov 25 08:48 foobar -> bar
[jwboyer@zod testdir]$ stat -L foobar
  File: ‘foobar’
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: 1fh/31d	Inode: 1496527     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/ jwboyer)   Gid: ( 1000/ jwboyer)
Context: unconfined_u:object_r:user_tmp_t:s0
Access: 2013-11-25 08:48:08.353347588 -0500
Modify: 2013-11-25 08:48:08.353347588 -0500
Change: 2013-11-25 08:48:08.353347588 -0500
 Birth: -
[jwboyer@zod testdir]$ sudo su -
Last failed login: Mon Nov 25 08:49:02 EST 2013 on pts/2
There were 2 failed login attempts since the last successful login.
[root@zod ~]# cd /tmp/testdir/
[root@zod testdir]# ls -l
total 0
-rw-rw-r--. 1 jwboyer jwboyer 0 Nov 25 08:48 bar
-rw-rw-r--. 1 jwboyer jwboyer 0 Nov 25 08:48 baz
-rw-rw-r--. 1 jwboyer jwboyer 0 Nov 25 08:48 biz
-rw-rw-r--. 1 jwboyer jwboyer 0 Nov 25 08:48 foo
lrwxrwxrwx. 1 jwboyer jwboyer 3 Nov 25 08:48 foobar -> bar
[root@zod testdir]# stat -L foobar
  File: ‘foobar’
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: 1fh/31d	Inode: 1496527     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/ jwboyer)   Gid: ( 1000/ jwboyer)
Context: unconfined_u:object_r:user_tmp_t:s0
Access: 2013-11-25 08:48:08.353347588 -0500
Modify: 2013-11-25 08:48:08.353347588 -0500
Change: 2013-11-25 08:48:08.353347588 -0500
 Birth: -
[root@zod testdir]# whoami
root
[root@zod testdir]# 

[jwboyer@zod ~]$ mkdir testdir
[jwboyer@zod ~]$ cd testdir
[jwboyer@zod testdir]$ ls
[jwboyer@zod testdir]$ ln -s foo bar
[jwboyer@zod testdir]$ stat -L bar
stat: cannot stat ‘bar’: No such file or directory
[jwboyer@zod testdir]$ sudo su -
Last failed login: Mon Nov 25 08:49:02 EST 2013 on pts/2
There were 2 failed login attempts since the last successful login.
[root@zod ~]# cd /home/jwboyer/
[root@zod jwboyer]# cd testdir/
[root@zod testdir]# ls
bar
[root@zod testdir]# ls -l
total 0
lrwxrwxrwx. 1 jwboyer jwboyer 3 Nov 25 08:51 bar -> foo
[root@zod testdir]# stat -L bar
stat: cannot stat ‘bar’: No such file or directory
[root@zod testdir]# 

Is there anything that seems related in dmesg on your system?

Comment 2 Andrew J. Schorr 2013-11-25 14:05:36 UTC
What kernel version are you running?  It does not work for me on tmpfs:

bash-4.2$ uname -r
3.11.8-200.fc19.x86_64
bash-4.2$ cd /tmp
bash-4.2$ touch foo
bash-4.2$ ln -s foo bar
bash-4.2$ ls -l foo bar
lrwxrwxrwx 1 schorr ead 3 Nov 25 08:59 bar -> foo
-rw-r--r-- 1 schorr ead 0 Nov 25 08:59 foo
bash-4.2$ stat -L bar
  File: ‘bar’
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: 1eh/30d Inode: 1428576     Links: 1
Access: (0644/-rw-r--r--)  Uid: (  300/  schorr)   Gid: (   50/     ead)
Access: 2013-11-25 08:59:03.353097727 -0500
Modify: 2013-11-25 08:59:03.353097727 -0500
Change: 2013-11-25 08:59:03.353097727 -0500
 Birth: -
bash-4.2$ sudo id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
bash-4.2$ sudo stat -L bar
stat: cannot stat ‘bar’: Permission denied
bash-4.2$ stat -f .
  File: "."
    ID: 0        Namelen: 255     Type: tmpfs
Block size: 4096       Fundamental block size: 4096
Blocks: Total: 505427     Free: 503048     Available: 503048
Inodes: Total: 505427     Free: 504205

There are no messages in dmesg, nor do I see anything in journalctl.

It also does not work on xfs:

bash-4.2$ cd /extra_disk/tmp
bash-4.2$ stat -f .
  File: "."
    ID: fd0200000000 Namelen: 255     Type: xfs
Block size: 4096       Fundamental block size: 4096
Blocks: Total: 15720960   Free: 6549785    Available: 6549785
Inodes: Total: 62914560   Free: 62853669
bash-4.2$ touch foo
bash-4.2$ ln -s foo bar
bash-4.2$ stat -L bar
  File: ‘bar’
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: fd02h/64770d    Inode: 67109800    Links: 1
Access: (0644/-rw-r--r--)  Uid: (  300/  schorr)   Gid: (   50/     ead)
Access: 2013-11-25 09:02:50.037690719 -0500
Modify: 2013-11-25 09:02:50.037690719 -0500
Change: 2013-11-25 09:02:50.037690719 -0500
 Birth: -
bash-4.2$ sudo stat -L bar
stat: cannot stat ‘bar’: Permission denied

Nor does it work on ext4:
bash-4.2$ cd /var/tmp
bash-4.2$ stat -f .
  File: "."
    ID: 1d76d12ed9db2b56 Namelen: 255     Type: ext2/ext3
Block size: 4096       Fundamental block size: 4096
Blocks: Total: 411098     Free: 171699     Available: 146304
Inodes: Total: 106496     Free: 90758
bash-4.2$ touch foo
bash-4.2$ ln -s foo bar
bash-4.2$ stat -L bar
  File: ‘bar’
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: fd06h/64774d    Inode: 274         Links: 1
Access: (0644/-rw-r--r--)  Uid: (  300/  schorr)   Gid: (   50/     ead)
Access: 2013-11-25 09:04:09.150992256 -0500
Modify: 2013-11-25 09:04:09.150992256 -0500
Change: 2013-11-25 09:04:09.150992256 -0500
 Birth: -
bash-4.2$ sudo stat -L bar
stat: cannot stat ‘bar’: Permission denied

Note: in case it matters, selinux is disabled:

bash-4.2$ grep SELINUX /etc/sysconfig/selinux 
# SELINUX= can take one of these three values:
SELINUX=disabled
# SELINUXTYPE= can take one of these two values:
SELINUXTYPE=targeted

Comment 3 Andrew J. Schorr 2013-11-25 14:48:05 UTC
This is to be expected, but the permission denied error also occurs for non-root users (other than the user that owns the link).  This is all very strange.

Comment 4 Andrew J. Schorr 2013-11-25 15:27:34 UTC
I tried enabling selinux on a laptop, but that did not help.

Comment 5 Josh Boyer 2013-11-25 16:21:05 UTC
Oh, this is the protected symlink stuff.  It's enabled by default on Fedora.  You can disable it by using the sysctl command or doing "echo 0 > /proc/sys/fs/protected_symlink" as root.  I must have had this disabled in my earlier testing for whatever reason.

Here's the commit log that brought in this change:

    fs: add link restrictions
    
    This adds symlink and hardlink restrictions to the Linux VFS.
    
    Symlinks:
    
    A long-standing class of security issues is the symlink-based
    time-of-check-time-of-use race, most commonly seen in world-writable
    directories like /tmp. The common method of exploitation of this flaw
    is to cross privilege boundaries when following a given symlink (i.e. a
    root process follows a symlink belonging to another user). For a likely
    incomplete list of hundreds of examples across the years, please see:
    http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
    
    The solution is to permit symlinks to only be followed when outside
    a sticky world-writable directory, or when the uid of the symlink and
    follower match, or when the directory owner matches the symlink's owner.
    
    Some pointers to the history of earlier discussion that I could find:
    
     1996 Aug, Zygo Blaxell
      http://marc.info/?l=bugtraq&m=87602167419830&w=2
     1996 Oct, Andrew Tridgell
      http://lkml.indiana.edu/hypermail/linux/kernel/9610.2/0086.html
     1997 Dec, Albert D Cahalan
      http://lkml.org/lkml/1997/12/16/4
     2005 Feb, Lorenzo Hernández García-Hierro
      http://lkml.indiana.edu/hypermail/linux/kernel/0502.0/1896.html
     2010 May, Kees Cook
      https://lkml.org/lkml/2010/5/30/144
    
    Past objections and rebuttals could be summarized as:
    
     - Violates POSIX.
       - POSIX didn't consider this situation and it's not useful to follow
         a broken specification at the cost of security.
     - Might break unknown applications that use this feature.
       - Applications that break because of the change are easy to spot and
         fix. Applications that are vulnerable to symlink ToCToU by not having
         the change aren't. Additionally, no applications have yet been found
         that rely on this behavior.
     - Applications should just use mkstemp() or O_CREATE|O_EXCL.
       - True, but applications are not perfect, and new software is written
         all the time that makes these mistakes; blocking this flaw at the
         kernel is a single solution to the entire class of vulnerability.
     - This should live in the core VFS.
       - This should live in an LSM. (https://lkml.org/lkml/2010/5/31/135)
     - This should live in an LSM.
       - This should live in the core VFS. (https://lkml.org/lkml/2010/8/2/188)
    
    Hardlinks:
    
    On systems that have user-writable directories on the same partition
    as system files, a long-standing class of security issues is the
    hardlink-based time-of-check-time-of-use race, most commonly seen in
    world-writable directories like /tmp. The common method of exploitation
    of this flaw is to cross privilege boundaries when following a given
    hardlink (i.e. a root process follows a hardlink created by another
    user). Additionally, an issue exists where users can "pin" a potentially
    vulnerable setuid/setgid file so that an administrator will not actually
    upgrade a system fully.
    
    The solution is to permit hardlinks to only be created when the user is
    already the existing file's owner, or if they already have read/write
    access to the existing file.
    
    Many Linux users are surprised when they learn they can link to files
    they have no access to, so this change appears to follow the doctrine
    of "least surprise". Additionally, this change does not violate POSIX,
    which states "the implementation may require that the calling process
    has permission to access the existing file"[1].
    
    This change is known to break some implementations of the "at" daemon,
    though the version used by Fedora and Ubuntu has been fixed[2] for
    a while. Otherwise, the change has been undisruptive while in use in
    Ubuntu for the last 1.5 years.
    
    [1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html
    [2] http://anonscm.debian.org/gitweb/?p=collab-maint/at.git;a=commitdiff;h=f4114656c3a6c6f6070e315ffdf940a49eda3279
    
    This patch is based on the patches in Openwall and grsecurity, along with
    suggestions from Al Viro. I have added a sysctl to enable the protected
    behavior, and documentation.
    
    Signed-off-by: Kees Cook <keescook@chromium.org>
    Acked-by: Ingo Molnar <mingo@elte.hu>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Comment 6 Andrew J. Schorr 2013-11-25 16:32:12 UTC
Sorry for the bother.  I wasn't aware of that.  This issue arose because I have a cron job that scans systems for dangling symlinks, and it could not dereference some symlinks in /var/tmp that point to /etc/cups/ppd/<printer>.ppd. Some desktop application must be creating those links, but root cannot chase them.


Note You need to log in before you can comment on or make changes to this bug.