diff options
Diffstat (limited to 'dev-lang/perl/files/perl-5.8.8-CAN-2005-0448-rmtree-2.patch')
-rw-r--r-- | dev-lang/perl/files/perl-5.8.8-CAN-2005-0448-rmtree-2.patch | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/dev-lang/perl/files/perl-5.8.8-CAN-2005-0448-rmtree-2.patch b/dev-lang/perl/files/perl-5.8.8-CAN-2005-0448-rmtree-2.patch new file mode 100644 index 000000000..d29fc2522 --- /dev/null +++ b/dev-lang/perl/files/perl-5.8.8-CAN-2005-0448-rmtree-2.patch @@ -0,0 +1,280 @@ +SECURITY [CAN-2005-0448]: + +Rewrite File::Path::rmtree to avoid race condition which allows an +attacker with write permission on directories in the tree being +removed to make files setuid or to remove arbitrary files (see +http://bugs.debian.org/286905 and http://bugs.debian.org/286922). + +Revised for 5.8.8-7etch6 to avoid failure when rmtree removes the +current directory. + +diff --git a/lib/File/Path.pm b/lib/File/Path.pm +index 2e41ff3..fd32b96 100644 +--- a/lib/File/Path.pm ++++ b/lib/File/Path.pm +@@ -72,33 +72,17 @@ or C<unlink> to remove it, or that it's skipping it. + + =item * + +-a boolean value, which if TRUE will cause C<rmtree> to +-skip any files to which you do not have delete access +-(if running under VMS) or write access (if running +-under another OS). This will change in the future when +-a criterion for 'delete permission' under OSs other +-than VMS is settled. (defaults to FALSE) ++a boolean value, which if FALSE (the default for non-root users) will ++cause C<rmtree> to adjust the mode of directories (if required) prior ++to attempting to remove the contents. Note that on interruption or ++failure of C<rmtree>, directories may be left with more permissive ++modes for the owner. + + =back + + It returns the number of files successfully deleted. Symlinks are + simply deleted and not followed. + +-B<NOTE:> There are race conditions internal to the implementation of +-C<rmtree> making it unsafe to use on directory trees which may be +-altered or moved while C<rmtree> is running, and in particular on any +-directory trees with any path components or subdirectories potentially +-writable by untrusted users. +- +-Additionally, if the third parameter is not TRUE and C<rmtree> is +-interrupted, it may leave files and directories with permissions altered +-to allow deletion (and older versions of this module would even set +-files and directories to world-read/writable!) +- +-Note also that the occurrence of errors in C<rmtree> can be determined I<only> +-by trapping diagnostic messages using C<$SIG{__WARN__}>; it is not apparent +-from the return value. +- + =head1 DIAGNOSTICS + + =over 4 +@@ -172,111 +156,127 @@ sub mkpath { + @created; + } + +-sub rmtree { +- my($roots, $verbose, $safe) = @_; +- my(@files); +- my($count) = 0; +- $verbose ||= 0; +- $safe ||= 0; ++sub _rmtree; ++sub _rmtree ++{ ++ my ($path, $prefix, $up, $up_dev, $up_ino, $verbose, $safe) = @_; ++ my $up_name = $up eq '..' ? 'parent' : 'initial'; ++ ++ my ($dev, $ino) = lstat $path or return 0; ++ unless (-d _) ++ { ++ print "unlink $prefix$path\n" if $verbose; ++ unless (unlink $path) ++ { ++ carp "Can't remove file $prefix$path ($!)"; ++ return 0; ++ } + +- if ( defined($roots) && length($roots) ) { +- $roots = [$roots] unless ref $roots; ++ return 1; + } +- else { +- carp "No root path(s) specified\n"; +- return 0; ++ ++ unless (chdir $path) ++ { ++ carp "Can't chdir to $prefix$path ($!)"; ++ return 0; + } + +- my($root); +- foreach $root (@{$roots}) { +- if ($Is_MacOS) { +- $root = ":$root" if $root !~ /:/; +- $root =~ s#([^:])\z#$1:#; +- } else { +- $root =~ s#/\z##; +- } +- (undef, undef, my $rp) = lstat $root or next; +- $rp &= 07777; # don't forget setuid, setgid, sticky bits +- if ( -d _ ) { +- # notabene: 0700 is for making readable in the first place, +- # it's also intended to change it to writable in case we have +- # to recurse in which case we are better than rm -rf for +- # subtrees with strange permissions +- chmod($rp | 0700, ($Is_VMS ? VMS::Filespec::fileify($root) : $root)) +- or carp "Can't make directory $root read+writeable: $!" +- unless $safe; +- +- if (opendir my $d, $root) { +- no strict 'refs'; +- if (!defined ${"\cTAINT"} or ${"\cTAINT"}) { +- # Blindly untaint dir names +- @files = map { /^(.*)$/s ; $1 } readdir $d; +- } else { +- @files = readdir $d; +- } +- closedir $d; +- } +- else { +- carp "Can't read $root: $!"; +- @files = (); +- } ++ # avoid a race condition where a directory may be replaced by a ++ # symlink between the lstat and the chdir ++ my ($new_dev, $new_ino, $perm) = stat '.'; ++ unless ("$new_dev:$new_ino" eq "$dev:$ino") ++ { ++ croak "Directory $prefix$path changed before chdir, aborting"; ++ } + +- # Deleting large numbers of files from VMS Files-11 filesystems +- # is faster if done in reverse ASCIIbetical order +- @files = reverse @files if $Is_VMS; +- ($root = VMS::Filespec::unixify($root)) =~ s#\.dir\z## if $Is_VMS; +- if ($Is_MacOS) { +- @files = map("$root$_", @files); +- } else { +- @files = map("$root/$_", grep $_!~/^\.{1,2}\z/s,@files); +- } +- $count += rmtree(\@files,$verbose,$safe); +- if ($safe && +- ($Is_VMS ? !&VMS::Filespec::candelete($root) : !-w $root)) { +- print "skipped $root\n" if $verbose; +- next; +- } +- chmod $rp | 0700, $root +- or carp "Can't make directory $root writeable: $!" +- if $force_writeable; +- print "rmdir $root\n" if $verbose; +- if (rmdir $root) { +- ++$count; +- } +- else { +- carp "Can't remove directory $root: $!"; +- chmod($rp, ($Is_VMS ? VMS::Filespec::fileify($root) : $root)) +- or carp("and can't restore permissions to " +- . sprintf("0%o",$rp) . "\n"); +- } +- } +- else { +- if ($safe && +- ($Is_VMS ? !&VMS::Filespec::candelete($root) +- : !(-l $root || -w $root))) +- { +- print "skipped $root\n" if $verbose; +- next; +- } +- chmod $rp | 0600, $root +- or carp "Can't make file $root writeable: $!" +- if $force_writeable; +- print "unlink $root\n" if $verbose; +- # delete all versions under VMS +- for (;;) { +- unless (unlink $root) { +- carp "Can't unlink file $root: $!"; +- if ($force_writeable) { +- chmod $rp, $root +- or carp("and can't restore permissions to " +- . sprintf("0%o",$rp) . "\n"); +- } +- last; +- } +- ++$count; +- last unless $Is_VMS && lstat $root; +- } ++ $perm &= 07777; ++ my $nperm = $perm | 0700; ++ unless ($safe or $nperm == $perm or chmod $nperm, '.') ++ { ++ carp "Can't make directory $prefix$path read+writeable ($!)"; ++ $nperm = $perm; ++ } ++ ++ my $count = 0; ++ if (opendir my $dir, '.') ++ { ++ my $entry; ++ while (defined ($entry = readdir $dir)) ++ { ++ next if $entry =~ /^\.\.?$/; ++ $entry =~ /^(.*)$/s; $entry = $1; # untaint ++ $count += _rmtree $entry, "$prefix$path/", '..', $dev, $ino, ++ $verbose, $safe; + } ++ ++ closedir $dir; ++ } ++ ++ # restore directory permissions if required (in case the rmdir ++ # below fails) now, while we're still in the directory and may do ++ # so without a race via '.' ++ unless ($nperm == $perm or chmod $perm, '.') ++ { ++ carp "Can't restore permissions on directory $prefix$path ($!)"; ++ } ++ ++ # don't leave the caller in an unexpected directory ++ unless (chdir $up) ++ { ++ croak "Can't return to $up_name directory from $prefix$path ($!)"; ++ } ++ ++ # ensure that a chdir .. didn't take us somewhere other than ++ # where we expected (see CVE-2002-0435) ++ unless (($new_dev, $new_ino) = stat '.' ++ and "$new_dev:$new_ino" eq "$up_dev:$up_ino") ++ { ++ croak "\u$up_name directory changed since entering $prefix$path"; ++ } ++ ++ print "rmdir $prefix$path\n" if $verbose; ++ if (rmdir $path) ++ { ++ $count++; ++ } ++ else ++ { ++ carp "Can't remove directory $prefix$path ($!)"; ++ } ++ ++ return $count; ++} ++ ++sub rmtree ++{ ++ my ($p, $verbose, $safe) = @_; ++ $p = [] unless defined $p and length $p; ++ $p = [ $p ] unless ref $p; ++ my @paths = grep defined && length, @$p; ++ ++ # default to "unsafe" for non-root (will chmod dirs) ++ $safe = $> ? 0 : 1 unless defined $safe; ++ ++ unless (@paths) ++ { ++ carp "No root path(s) specified"; ++ return; ++ } ++ ++ opendir my $oldpwd, '.' or do { ++ carp "Can't fetch initial working directory"; ++ return; ++ }; ++ ++ my ($dev, $ino) = stat '.' or do { ++ carp "Can't stat initial working directory"; ++ return; ++ }; ++ ++ my $count = 0; ++ for my $path (@paths) ++ { ++ $count += _rmtree $path, '', $oldpwd, $dev, $ino, $verbose, $safe; + } + + $count; |