LWN.net Logo

Standards, the kernel, and Postfix

Standards, the kernel, and Postfix

Posted Aug 21, 2008 6:37 UTC (Thu) by magnus (subscriber, #34778)
Parent article: Standards, the kernel, and Postfix

I find it a bit odd that a normal user can do things like this:
ln /etc/shadow ~/myfile
and control where system files show up in the file system. 

Would something break if hardlinking to files not owned by yourself would be forbidden?


(Log in to post comments)

Standards, the kernel, and Postfix

Posted Aug 21, 2008 8:58 UTC (Thu) by adegert (subscriber, #5503) [Link]

its even more odd if you create the link in a directory which has the sticky bit set:

ln /etc/shadow /tmp/myfile

you can create that link, but you can't delete it afterwards (only root can do that)

Standards, the kernel, and Postfix

Posted Aug 21, 2008 15:43 UTC (Thu) by foom (subscriber, #14868) [Link]

> ln /etc/shadow /tmp/myfile
> you can create that link, but you can't delete it afterwards (only root can do that)

Wow, that's insane!

I'd never thought about this issue of hardlinks...but it kinda seems bad that
you can create a file  owned by another user.

Standards, the kernel, and Postfix

Posted Aug 22, 2008 23:44 UTC (Fri) by giraffedata (subscriber, #1954) [Link]

it kinda seems bad that you can create a file owned by another user.

You can't create a file owned by another user. But you can create a directory entry that points to a file owned by another user.

It all seems strange compared to what you can do with symbolic links, but if you think about the early systems that didn't have symbolic links, you can see how useful it is.

I think what would have given me pause in the pre-symbolic-link days was not that I could keep a pointer to your file in my directory, but that I could prevent you from deleting that file. So while I can't create a file owned by you, I can force you to keep owning one that only I am using. If the system does accounting, you have to pay for the space.

Altogether, I think Unix should have either made files and their names completely separate, like VMS, or completely merged, like NTFS. The intermediate thing has too many surprises.

Standards, the kernel, and Postfix

Posted Aug 30, 2008 14:37 UTC (Sat) by abpsoft (guest, #53672) [Link]

Hi,

> its even more odd if you create the link in a directory which has the sticky bit set:
> ln /etc/shadow /tmp/myfile
> you can create that link, but you can't delete it afterwards (only root can do that)

Hmpf.

Actually I consider this to be an implementation bug of the directory sticky bit semantics. It should prevent from happening *any* changes to the directory involving names to inodes the subject process doesn't own. Most users just think about unlink(2)ing files, but it also prevents rename(2) from succeeding. Not blocking directory entry creation through link(2) in the first place thus should be considered incomplete implementation, or is there explicit standard language stating otherwise? I haven't read much beyond XPG4 ;)

Andre.

Standards, the kernel, and Postfix

Posted Aug 21, 2008 10:27 UTC (Thu) by rwmj (subscriber, #5474) [Link]

I find it a bit odd that a normal user can do things like this: ln /etc/shadow ~/myfile and control where system files show up in the file system.

Not just odd, but a security problem too. A user can do ln /usr/sbin/sendmail ~/sendmail then wait for a security bug to be reported in sendmail. Even though the administrator upgrades /usr/sbin/sendmail the buggy setuid sendmail is still available in the user's home directory.

This is one reason to have separate root and /usr partitions, because hardlinking across filesystems isn't possible.

Rich.

Standards, the kernel, and Postfix

Posted Aug 21, 2008 12:08 UTC (Thu) by epa (subscriber, #39769) [Link]

By golly, you're right!

% su
# cp /bin/true /test
# chmod u+s /test
# exit
% ln /test ~/suid_program
% su
# rm /test
# exit
% ls -l ~/suid_program
-rwsr-xr-x 1 root root 26468 2008-08-21 13:06 suid_program

Yikes!

Perhaps this indicates the need for a 'delete' system call which makes sure a file is really
gone, rather than merely 'unlink'ing one of the names which points to it.

Standards, the kernel, and Postfix

Posted Aug 21, 2008 13:59 UTC (Thu) by Los__D (guest, #15263) [Link]

I guess it would also be a (small) improvement for package managers to overwrite existing
files, instead of removing the old one first.

Standards, the kernel, and Postfix

Posted Aug 21, 2008 14:20 UTC (Thu) by djpig (subscriber, #18768) [Link]

I guess it would also be a (small) improvement for package managers to overwrite existing files, instead of removing the old one first.

Uh, you better not have any programs running during the update then...

Standards, the kernel, and Postfix

Posted Aug 21, 2008 14:28 UTC (Thu) by epa (subscriber, #39769) [Link]

Exactly.  The relatively smooth upgrading of software on Unix-like systems is because you can
unlink the old file and create a new file with the same name.  The old one will continue to
exist, with its old data, as long as a process has it open.  If you try to fix the suid binary
problem by forcibly removing a file, or overwriting its contents, then you risk breaking
running code.

(Since writing bytes to a file is not atomic, you could theoretically introduce new security
holes by overwriting the contents of a suid binary.  What if someone runs it halfway through
the update, when it contains a mixture of old and new code?)

One answer would be to move the suid flag out of the inode and put it in the directory entry.
Then you could make as many links to a file as you wanted, but you wouldn't be able to get the
suid magic (unless, of course, you have permission to set the suid bit anyway).  But given the
conservatism of Unix-like systems and the large amount of filesystem code that would have to
change, I can't see this happening soon.

Standards, the kernel, and Postfix

Posted Aug 21, 2008 14:44 UTC (Thu) by rwmj (subscriber, #5474) [Link]

No, package managers should just remove the setuid bit before unlinking the file.

This doesn't affect running programs. It does affect someone who starts to run the program at the moment that the suid bit is removed, but this is already a problem during package upgrades (the file is temporarily removed, so attempts to run it can fail briefly).

It's actually possible that package managers do this correctly already, since this problem is very old and well-known.

Rich.

Standards, the kernel, and Postfix

Posted Aug 21, 2008 18:48 UTC (Thu) by vmole (guest, #111) [Link]

No, package managers should put the file in the proper directory with a unique name (e.g. foo.package_new) with proper permissions and ownership, and then rename(2) the proper name to the unique name. This way there's never a time when '/bin/foo' is anything but the proper old entry or the proper new entry, since rename(2) is atomic. Exisiting users of the file won't see the change - they've got an open descriptor to the old file, which remains on disk until the last open descriptor is closed.

How to replace an executable...

Posted Aug 22, 2008 6:08 UTC (Fri) by faramir (subscriber, #2327) [Link]

There seem to be three issues:

1. Atomic replacement of an executable (by name)
2. Removing permissions from potentially hard linked old copies (by inode)
3. Reporting possible nefarious activities by hard linkers.

I think this sequence deals with them all:

1. Hard link canonical name of old executable to first temporary name.
2. Create new executable with second temporary name and appropriate privs.
3. rename() second temporary name to canonical name (this is atomic).
4. Chown/chmod first temporary name to remove any special privileges, execute bits, etc.
5. stat() first temporary name and report if there is more then one link
(Should be just one from first temporary name at this point).
6. Unlink first temporary name.

At the end of the sequence, you will have atomically replaced the executable, removed any
special privileges from any links people may have hidden away, and reported (to the extent
possible without a full search of the filesystem) that somebody has been linking to
executables.

Of course, the sendmail example is a bad one as typically there is more then one valid link.
'sendmail' and 'newaliases were traditionally the same program.  I know of no way to atomically
make a bunch of links to one
program so the best I can think of would be to make a bunch of temporary names for the new
execuable and then rename() them all into place sequentially in steps 2 and 3.  Steps 4-6
could remain the same and would still be able to accurately report improper links while at the
same time making them useless.





Standards, the kernel, and Postfix

Posted Aug 21, 2008 15:12 UTC (Thu) by epa (subscriber, #39769) [Link]

Here is a handy tool to make a collection of hard links to suid or sgid binaries and keep the
collection up to date.  Use as

% mkdir suid_collection
% suid_harvester /sbin /bin /usr/sbin /usr/bin /usr/local/bin suid_collection

#!/usr/bin/perl
use warnings;
use strict;
use 5.010;
use File::Find;
use File::stat;
use Fcntl qw(:mode);
use File::Spec;

sub interesting {
    my $sb = shift;
    my $mode = $sb->mode;
    my $suid = $mode & S_ISUID;
    my $sgid = $mode & S_ISGID;
    my $uid = $sb->uid;
    my $gid = $sb->gid;
    my $xusr = $mode & S_IXUSR;
    my $xgrp = $mode & S_IXGRP;
    my $xoth = $mode & S_IXOTH;

    if ($suid) {
	if ($xoth) {
	    # suid, executable by all.
	    return 1;
	}
	elsif ($xgrp) {
	    # suid, executable by some group.
	    return 1;
	}
    }
    elsif ($sgid) {
	if ($xoth) {
	    # sgid, executable by all.
	    return 1;
	}
	elsif ($uid != 0 and $xusr) {
	    # suid, executable by someone non-root.
	    return 1;
	}
    }
    return 0;
}

die "usage: $0 source... dest\n"
  if @ARGV < 2;
my $dest = pop @ARGV;
my @source = @ARGV;
my $dest_abs = File::Spec->rel2abs($dest);
chdir $dest or die "cannot chdir to $dest: $!";
my %already;
foreach (glob '*') {
    if (/\A([0-9]+)[.].+\z/) {
	my $ino = $1;
	$already{$ino}++ && warn "two files in $dest beginning $ino.\n";
	my $sb = lstat $_;
	warn("cannot lstat $dest/$_, skipping\n"), next if not $sb;
	if (not interesting $sb) {
	    warn "sadly, $_ no longer has useful permissions\n";
	}
    }
}
sub for_each_file {
    my $sb = lstat $_;
    warn("cannot lstat $File::Find::name, skipping\n"), return if not $sb;
    my $ino = $sb->ino;
    $already{$ino}++ && return;
    return if not interesting $sb;
    my $link_to = "$ino.$_";
    say "$File::Find::name -> $link_to";
    link $File::Find::name, "$dest_abs/$link_to"
      or warn "cannot link to $link_to: $!\n";
}
find \&for_each_file, @source;

"delete()" system call unnecessary ... for this case

Posted Aug 21, 2008 20:59 UTC (Thu) by AnswerGuy (guest, #1256) [Link]


 Actually the really savvy admin, or patch/fix script author, would perform  a sanity check on
files to be removed by the patch, opening it, fstat()ing  it, chmod() it to remove SUID/SGID
bits, unlink()ing it, and checking to ensure that the next fstat() returns a  link count of
zero.  If that fails (someone else has a hard link to it) then  over-write the file contents
with an program which does the following:

  * syslog()s the user and the command argument
  * optionally warns the user that they are attempting to use an out-dated version of the
program (and advising them against using hard links to system binaries in general?)
  * optionally wraps the new binary (execve()'s it) or exits


Standards, the kernel, and Postfix

Posted Aug 23, 2008 0:04 UTC (Sat) by giraffedata (subscriber, #1954) [Link]

Perhaps this indicates the need for a 'delete' system call which makes sure a file is really gone

You're talking about a fundamentally different file model, then. Something like VMS, where directories are an independent layer above the flat filesytem. Or like NTFS where files actually have names so you don't have a somewhat separate lookup facility (directory tree) for them.

Or even just a world where only one link to a file is allowed.

I think those would all be preferable to what we have, but if you're interested in something that can be compatibly glued on to what exists, then some kind of "delete" system call probably isn't better than just truncating the file to nothing.

Standards, the kernel, and Postfix

Posted Aug 21, 2008 15:34 UTC (Thu) by mb (subscriber, #50428) [Link]

>>I find it a bit odd that a normal user can do things like this: ln /etc/shadow ~/myfile and
control where system files show up in the file system.
>
> Not just odd, but a security problem too. 

This is why the package manager tool, that replaces the application, should first remove all
read/write/exec permissions from the file before unlinking it. This way the hardlink won't be
usable by the attacker anymore, as he can't execute it anymore.
A call to revoke() might be needed, too, to close all currently open mmap(). I'm not sure on
that for regular files...

I don't know whether apt/rpm actually do this.

But udev, for example, uses this to avoid attacks on /dev files.

Copyright © 2013, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds