Subject: Re: bug#17103: regression: cp -al doesn't copy symlinks, but tries to link to them (fail)

From: Pádraig Brady <[email-masked]>
Date: Wed, 26 Mar 2014 20:54:38 +0000

On 03/26/2014 08:21 PM, Linda Walsh wrote:
> 
> 
> Pádraig Brady wrote:
>> On 03/26/2014 06:08 PM, Linda Walsh wrote:
>>> have a simple test case:
>>> as root (w /umask 002):
>>>
>>> mkdir -p dir/{a,b}
>>> touch dir/b/file
>>> ln -s ../b/file dir/a/symfile
>>> ---
>>>
>>> So now tree should look like:
>>>
>>>>  tree -AFugp dir
>>> dir
>>> +-- [drwxrwxr-x root     root    ]  a/
>>> |   +-- [lrwxrwxrwx root     root    ]  symfile -> ../b/file
>>> +-- [drwxrwxr-x root     root    ]  b/
>>>     +-- [-rw-rw-r-- root     root    ]  file
>>> ----
>>>
>>> Now, w/normal user, who is in group root, try:
>>>
>>> cp -al dir dir2
>>> cp: cannot create hard link ‘dir2/dir/a/symfile’ to ‘dir/a/symfile’: Operation not permitted
>>> -----
>>>
>>> Trying to link to a symlink is the bug --
>>> it used to duplicate the symlink.
>>>
>>> This is a recent behavior
>>> change -- i.e. looking at earlier behavior, the symlinks,
>>> like the directories are created as the 'user', and
>>> only files are linked to.
>>>
>>> Core utils version: 8.21 (suse rpm coreutils-8.21-7.7.7.x86_64)
>>>
>>> Any idea how this managed to be broken?
>>
>> So I think the change to use hardlinks to symlinks rather than new symlinks happened with:
>> http://git.sv.gnu.org/gitweb/?p=coreutils.git;a=commitdiff;h=594292a1
>>
>> I.E. The "new symlink" behaviour only happened between v8.0 and v8.10 inclusive.
>>
>> So why is the hardlink to symlink being disallowed?
>> I wonder is it due to "protected_hardlinks":
>> http://danwalsh.livejournal.com/64493.html
> ----
>     As far as I know, you could never hardlink
> to a symlink.  only to a file.  A symlink is more like
> a directory in that regard.
> 
> Looking at the article, it doesn't seem it should apply...it
> says
> 1) The solution is to permit symlinks to only be followed when outside a sticky world-writable directory,
>    [it isn't in 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.
>     [in the created dir, the symlink and directory owner match]
> 2) 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.
>    [ Already have r/w access to the file via being in group root and the group
> having write access]
> 
> I think I did run into this change, though, and because of it, my system
> is less secure.  ... I.e. in my use case, am copying a source tree
> into a 2nd tree.  I am the directory owner of all dirs in the 2nd tree,
> but all the source files should allow my linking to it as the permissions
> on the inode protect the contents of the inode.  The access to create a
> hardlink to that inode has always been controlled by the directory owner,
> but it never gives them write access to the content.
> 
> Their exploit case was for a stupid admin who chowned all files in a user's dir "for them" -- why would that EVER be done?   I.e. the root admin should
> be immediately suspicious as to why they'd need that done.  Since you
> can't hardlink directories, the only way for a foreign owned directory
> to get into someone's home space, would be if they opened the permissions
> on the parent then later closed them.
> 
> The whole premise of their change relies on the user tricking the admin.
> 
> But if that's so easy, the user already has root access, effectively,
> and the games up.
> 
> 
>>
>> If we fell back to using symlinks, would that only
>> push the perm issue on to when the symlink was followed?
> ----
>     No, the file it points to, if you look at my example
> is rw for the 'group' members.

That is true, but I confirmed that this is caused by "protected_hardlinks"
Perhaps there is a blanket ban on symlinks if you're not the owner,
since the symlink could be later changed to point somewhere more sensitive?
Kees do you know if this is the case?

$ ln dir/b/file flink
$ ln dir/a/symfile slink
ln: failed to create hard link ‘slink’ => ‘dir/a/symfile’: Operation not permitted

tp2:lt$ echo 0 | sudo tee /proc/sys/fs/protected_hardlinks
0
$ ln dir/a/symfile slink
$ rm slink

# And to demonstrate that "protected_symlinks" doesn't seem to be significant here:
$ echo 1 | sudo tee /proc/sys/fs/protected_hardlinks
1
$ echo 0 | sudo tee /proc/sys/fs/protected_symlinks
0
$ ln dir/a/symfile slink
ln: failed to create hard link ‘slink’ => ‘dir/a/symfile’: Operation not permitted

So should we fall back to our symlink emulation if we get EPERM from linkat() ?
I'd like to get some more info on this behavior before doing that though.

thanks,
Pádraig.