8.17.3. Discussion
Usually you trust users to set file permissions as they wish. If they
want others to read their files, or even to write to them, that's
their business. Applications such as editors, mailers, and shells are
often more discerning, though, refusing to evaluate code in
configuration files if anyone but the owner can write to them. This
helps avoid Trojan horse attacks. Security-minded programs such as
ftp and ssh may even reject
config files that can be read by anyone but their owner.
If the file is writable by someone other than the owner or is owned
by someone other than the current user or the superuser, it shouldn't
be trusted. To figure out file ownership and permissions, the
stat function is used. The following function
returns true if the file is deemed safe and false otherwise. If the
stat fails, undef is returned.
use File::stat;
sub is_safe {
my $path = shift;
my $info = stat($path);
return unless $info;
# owner neither superuser nor me
# the real uid is in stored in the $< variable
if (($info->uid != 0) && ($info->uid != $<)) {
return 0;
}
# check whether group or other can write file.
# use 066 to detect either reading or writing
if ($info->mode & 022) { # someone else can write this
return 0 unless -d _; # non-directories aren't safe
# but directories with the sticky bit (01000) are
return 0 unless $info->mode & 01000;
}
return 1;
}
A directory is considered safe even if others can write to it,
provided its mode 01000 (owner delete only) bit is set.
use Cwd;
use POSIX qw(sysconf _PC_CHOWN_RESTRICTED);
sub is_verysafe {
my $path = shift;
return is_safe($path) if sysconf(_PC_CHOWN_RESTRICTED);
$path = getcwd( ) . "/" . $path if $path !~ m{^/};
do {
return unless is_safe($path);
$path =~ s#([^/]+|/)$##; # dirname
$path =~ s#/$## if length($path) > 1; # last slash
} while length $path;
return 1;
}
To use this in a program, try something like this:
$file = "$ENV{HOME}/.myprogrc";
readconfig($file) if is_safe($file);
This has potential for a race condition, because it's presumed that
the hypothetical readconfig function will open the
file. Between the time when is_safe checks the
file's stats and when readconfig opens it,
something wicked could theoretically occur. To avoid this, pass
is_safe the already open filehandle, which is set
up to handle this:
$file = "$ENV{HOME}/.myprogrc";
if (open(FILE, "<", $file)) {
readconfig(*FILE) if is_safe(*FILE);
}
You would still have to arrange for readconfig to
accept a filehandle instead of a filename, though.