[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[SCM] Debian package checker branch, wheezy, updated. 2.5.10.4-15-ge0dc594



The following commit has been merged in the wheezy branch:
commit e725c588cb6091461a1f0c52c9f383109163bf22
Author: Niels Thykier <niels@thykier.net>
Date:   Fri Apr 5 10:01:27 2013 +0200

    L::Util: Add is_ancestor_of function
    
    This function can test if a given path is "contained" within a given
    dir (or is the dir itself).
    
    Signed-off-by: Niels Thykier <niels@thykier.net>

diff --git a/debian/changelog b/debian/changelog
index 2292bd2..126ffd2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -16,6 +16,10 @@ lintian (2.5.10.5) unstable; urgency=low
     + [NT] Fix path traversal issue that could leak information
       about the host system.
 
+  * lib/Lintian/Util.pm:
+    + [NT] Add sub to check if a path is contained within a given dir.
+
+
  -- Niels Thykier <niels@thykier.net>  Fri, 05 Apr 2013 17:15:00 +0200
 
 lintian (2.5.10.4) unstable; urgency=low
diff --git a/lib/Lintian/Collect/Package.pm b/lib/Lintian/Collect/Package.pm
index ce0cef2..6e879cd 100644
--- a/lib/Lintian/Collect/Package.pm
+++ b/lib/Lintian/Collect/Package.pm
@@ -288,32 +288,41 @@ unpacked.  If C<$name> is given, it will return the path to that
 specific file (or dir).  The method will strip any leading "./" and
 "/" from C<$name>, but it will not check if C<$name> actually exists
 nor will it check for path traversals.
-  Caller is responsible for checking the sanity of the path passed to
-unpacked and verifying that the returned path points to the expected
-file.
 
 The path returned is not guaranteed to be inside the Lintian Lab as
 the package may have been unpacked outside the Lab (e.g. as
 optimization).
 
+Caveat with symlinks: Package is extracted as is and the path returned
+by this method points to the extracted file object.  If this is a
+symlink, it may "escape the root" and point to a file outside the lab
+(and a path traversal).
+
 The following code may be helpful in checking for path traversal:
 
- use Cwd qw(realpath);
+ use Lintian::Util qw(is_ancestor_of);
 
  my $collect = ... ;
  my $file = '../../../etc/passwd';
- # Append slash to follow symlink if $collect->unpacked returns a symlink
- my $uroot = realpath($collect->unpacked() . '/');
- my $ufile = realpath($collect->unpacked($file));
- if ($ufile =~ m,^$uroot,) {
+ my $uroot = $collect->unpacked;
+ my $ufile = $collect->unpacked($file);
+ # $uroot will exist, but $ufile might not.
+ if ( -e $ufile && is_ancestor_of($uroot, $ufile)) {
     # has not escaped $uroot
     do_stuff($ufile);
- } else {
+ } elsif ( -e $ufile) {
     # escaped $uroot
     die "Possibly path traversal ($file)";
+ } else {
+    # Does not exists
  }
 
-Alternatively one can use Lintian::Util::resolve_pkg_path.
+Alternatively one can use resolve_pkg_path in L<Lintian::Util> or
+L<link_resolved|Lintian::Path/link_resolved>.
+
+To get a list of entries in the package or the file meta data of the
+entries (as L<path objects|Lintian::Path>), see L</sorted_index> and
+L</index (FILE)>.
 
 =item file_info
 
diff --git a/lib/Lintian/Util.pm b/lib/Lintian/Util.pm
index 293ec62..38ec67f 100644
--- a/lib/Lintian/Util.pm
+++ b/lib/Lintian/Util.pm
@@ -24,6 +24,8 @@ use strict;
 use warnings;
 
 use base 'Exporter';
+use Carp qw(croak);
+use Cwd qw(abs_path);
 
 use constant {
   DCTRL_DEBCONF_TEMPLATE => 1,
@@ -64,7 +66,8 @@ BEGIN {
                  perm2oct
                  check_path
                  clean_env
-                 resolve_pkg_path),
+                 resolve_pkg_path
+                 is_ancestor_of),
                  @{ $EXPORT_TAGS{constants} }
     );
 
@@ -1060,6 +1063,38 @@ sub resolve_pkg_path {
     return join '/', @cc;
 }
 
+=item is_ancestor_of(PARENTDIR, PATH)
+
+Returns true if and only if PATH is PARENTDIR or a path stored
+somewhere within PARENTDIR (or its subdirs).
+
+This function will resolve the paths; any failure to resolve the path
+will cause a trappable error.
+
+=cut
+
+sub is_ancestor_of {
+    my ($ancestor, $file) = @_;
+    my $resolved_file = abs_path($file)
+        // croak("resolving $file failed: $!");
+    my $resolved_ancestor = abs_path($ancestor)
+        // croak("resolving $ancestor failed: $!");
+    my $len;
+    return 1 if $resolved_ancestor eq $resolved_file;
+    # add a slash, "path/some-dir" is not "path/some-dir-2" and this
+    # allows us to blindly match against the root dir.
+    $resolved_file .= '/';
+    $resolved_ancestor .= '/';
+
+    # If $resolved_file is contained within $resolved_ancestor, then
+    # $resolved_ancestor will be a prefix of $resolved_file.
+    $len = length($resolved_ancestor);
+    if (substr($resolved_file, 0, $len) eq $resolved_ancestor) {
+        return 1;
+    }
+    return 0;
+}
+
 =back
 
 =head1 SEE ALSO

-- 
Debian package checker


Reply to: