User: Password:
|
|
Subscribe / Log in / New account

Standards, the kernel, and Postfix

Standards, the kernel, and Postfix

Posted Aug 21, 2008 12:08 UTC (Thu) by epa (subscriber, #39769)
In reply to: Standards, the kernel, and Postfix by rwmj
Parent article: Standards, the kernel, and Postfix

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.


(Log in to post comments)

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 (guest, #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 (subscriber, #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.


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