[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 32d264977a975183b4b22435e56ab13011145717
Author: Niels Thykier <niels@thykier.net>
Date:   Fri Apr 5 16:20:14 2013 +0200

    checks/*: Check for symlinks before opening files
    
    Signed-off-by: Niels Thykier <niels@thykier.net>

diff --git a/checks/cruft b/checks/cruft
index ece468f..a9fe63d 100644
--- a/checks/cruft
+++ b/checks/cruft
@@ -29,7 +29,7 @@ use warnings;
 use Lintian::Data;
 use Lintian::Relation ();
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail);
+use Lintian::Util qw(fail is_ancestor_of);
 
 use Cwd;
 use File::Find;
@@ -350,6 +350,10 @@ sub find_cruft {
         }
     }
     -f or return; # we just need normal files for the rest
+    if (-l && !is_ancestor_of($info->unpacked, $_)) {
+        # skip unsafe symlinks too
+        return;
+    }
 
     unless ($warned->{$name}) {
         for my $rule (@file_checks) {
diff --git a/checks/debhelper b/checks/debhelper
index 4e2b960..0173588 100644
--- a/checks/debhelper
+++ b/checks/debhelper
@@ -25,7 +25,7 @@ use warnings;
 use Lintian::Data;
 use Lintian::Relation;
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail slurp_entire_file);
+use Lintian::Util qw(fail is_ancestor_of slurp_entire_file);
 
 # If compat is less than or equal to this, then a missing version
 # for this level is only a pedantic issue.
@@ -88,6 +88,11 @@ my $seen_dh = 0;
 my $seen_python_helper = 0;
 my $seen_python3_helper = 0;
 
+if ( ! -f "$droot/rules" or !is_ancestor_of($droot, "$droot/rules")) {
+    # unsafe symlink
+    return;
+}
+
 open(RULES, '<', "$droot/rules") or fail("cannot read debian/rules: $!");
 
 while (<RULES>) {
@@ -297,6 +302,7 @@ foreach my $file (sort readdir(DEBIAN)) {
     next if $file eq 'rules' or not -f "$droot/$file";
     if ($file =~ m/^(?:(.*)\.)?(?:post|pre)(?:inst|rm)$/) {
         next unless $needtomodifyscripts;
+        next unless is_ancestor_of($droot, "$droot/$file");
 
         # They need to have #DEBHELPER# in their scripts.  Search for scripts
         # that look like maintainer scripts and make sure the token is there.
@@ -338,6 +344,7 @@ foreach my $file (sort readdir(DEBIAN)) {
         # Check whether this is a debhelper config file that takes a list of
         # filenames.
         if ($filename_configs->known($base)) {
+            next unless is_ancestor_of($droot, "$droot/$file");
             if ($level < 9) {
                 # debhelper only use executable files in compat 9
                 _tag_if_executable ($file, "$droot/$file");
diff --git a/checks/infofiles b/checks/infofiles
index 5a1a254..3d8266c 100644
--- a/checks/infofiles
+++ b/checks/infofiles
@@ -79,6 +79,10 @@ foreach my $file ($info->sorted_index) {
     # If this is the main info file (no numeric extension). make sure it has
     # appropriate dir entry information.
     if ($fname !~ /-\d+\.gz/ && $file_info =~ /gzip compressed data/) {
+        if ($index_info->is_symlink && !is_ancestor_of($info->unpacked, $file)) {
+            # unsafe symlink, skip
+            next;
+        }
         my $pid = open INFO, '-|';
         if (not defined $pid) {
             fail("cannot fork: $!");
diff --git a/checks/init.d b/checks/init.d
index abab5df..84d8776 100644
--- a/checks/init.d
+++ b/checks/init.d
@@ -22,6 +22,8 @@ package Lintian::init_d;
 use strict;
 use warnings;
 
+use File::Basename qw(dirname);
+
 use Lintian::Check qw($PKGNAME_REGEX);
 use Lintian::Tags qw(tag);
 use Lintian::Util qw(fail);
@@ -243,6 +245,11 @@ sub check_init {
         if ($target =~ m,(?:\A|/)lib/init/upstart-job\z,) {
             return;
         }
+        if (!is_ancestor_of(dirname($initd_file), $initd_file)) {
+            # unsafe symlink, skip.  NB: dirname($initd_file) is safe
+            # because coll/init.d does sanity checking for us.
+            return;
+        }
     }
     open(IN, '<', $initd_file)
         or fail("cannot open init.d file $initd_file: $!");
diff --git a/checks/menu-format b/checks/menu-format
index fcee4e9..b947840 100644
--- a/checks/menu-format
+++ b/checks/menu-format
@@ -37,7 +37,7 @@ use strict;
 use warnings;
 
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail);
+use Lintian::Util qw(fail is_ancestor_of);
 
 use File::Basename;
 
@@ -373,6 +373,7 @@ foreach my $menufile (@menufiles) {
     $fullname = "usr/lib/menu/$basename" if $menufile =~ m,^menu/lib/,o;
 
     next if $basename eq 'README'; # README is a special case
+    next if !is_ancestor_of('menu', $menufile);
 
     my $menufile_line ='';
     open (IN, '<', $menufile) or
@@ -655,6 +656,7 @@ sub VerifyIcon {
     }
 
     # Try the explicit location, and if that fails, try the standard path.
+    my $pkgroot = $info->unpacked;
     my $iconfile = $info->unpacked($icon);
     if (! -f $iconfile) {
         $iconfile = $info->unpacked("/usr/share/pixmaps/$icon");
@@ -662,6 +664,7 @@ sub VerifyIcon {
             my $ginfo = $group->info;
             foreach my $depproc (@{ $ginfo->direct_dependencies ($proc) }) {
                 my $dinfo = $depproc->info;
+                $pkgroot = $dinfo->unpacked;
                 $iconfile = $dinfo->unpacked($icon);
                 last if -f $iconfile;
                 $iconfile = $info->unpacked("/usr/share/pixmaps/$icon");
@@ -670,11 +673,19 @@ sub VerifyIcon {
         }
     }
 
-    if (! open (IN, '<', $iconfile)) {
+    # Last stat is a -f from above, reuse it
+    if (-e _) {
+        if (!is_ancestor_of($pkgroot, $iconfile)) {
+            # unsafe symlink
+            return;
+        }
+    } else {
         tag 'menu-icon-missing', $icon;
         return;
     }
 
+    open (IN, '<', $iconfile) or fail "open $iconfile: $!";
+
     my $parse = 'XPM header';
     my $line;
     do { defined ($line = <IN>) or goto parse_error; }
diff --git a/checks/menus b/checks/menus
index 9803a71..6fa0781 100644
--- a/checks/menus
+++ b/checks/menus
@@ -180,7 +180,8 @@ if ($docbase_file) {
     my $dbfile;
     while (defined ($dbfile = readdir DOCBASEDIR)) {
         # don't try to parse executables, plus we already warned about it
-        next if -x "doc-base/$dbfile";
+        # - skip symlinks as well, unlikely to be used for real doc-base files.
+        next if -x "doc-base/$dbfile" or -l "doc-base/$dbfile";
         check_doc_base_file($dbfile, $pkg, $type, \%all_files, \%all_links);
     }
     closedir DOCBASEDIR;
diff --git a/checks/patch-systems b/checks/patch-systems
index f492c16..ec1c5fe 100644
--- a/checks/patch-systems
+++ b/checks/patch-systems
@@ -24,7 +24,7 @@ use strict;
 use warnings;
 
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail);
+use Lintian::Util qw(fail is_ancestor_of);
 use Cwd qw(realpath);
 
 sub run {
@@ -43,18 +43,27 @@ sub run {
     }
     my $quilt_format = ($format =~ /3\.\d+ \(quilt\)/) ? 1 : 0;
 
-    my $droot = realpath($info->debfiles);
+    my $droot = $info->debfiles;
     my $dpdir = "$droot/patches";
+    if (!is_ancestor_of($droot, $dpdir)) {
+        # Bad symlink
+        return;
+    }
 
     #----- dpatch
     if ($build_deps->implies("dpatch")) {
         $uses_patch_system++;
         #check for a debian/patches file:
-        if (! -r "$dpdir/00list") {
+        if (-l "$dpdir/00list" and not is_ancestor_of($droot, "$dpdir/00list")) {
+            # skip
+        } elsif (! -r "$dpdir/00list") {
             tag 'dpatch-build-dep-but-no-patch-list';
         } else {
             my $list_uses_cpp = 0;
-            if (open(OPTS, '<', "$dpdir/00options")) {
+            if (-f "$dpdir/00options"
+                  && is_ancestor_of($droot, "$dpdir/00options")) {
+                open(OPTS, '<', "$dpdir/00options")
+                    or fail "open 00options: $!";
                 while(<OPTS>) {
                     if (/DPATCH_OPTION_CPP=1/) {
                         $list_uses_cpp = 1;
@@ -65,7 +74,9 @@ sub run {
             }
             foreach my $listfile (glob("$dpdir/00list*")) {
                 my @patches;
-                if (open(IN, '<', $listfile)) {
+                if (-f $listfile and is_ancestor_of($droot, $listfile)) {
+                    open(IN, '<', $listfile)
+                        or fail "open $listfile: $!";
                     while(<IN>) {
                         chomp;
                         next if (/^\#/); #ignore comments or CPP directive
@@ -86,9 +97,8 @@ sub run {
                     $patch_file .= '.dpatch' if -e "$dpdir/$patch_file.dpatch"
                         and not -e "$dpdir/$patch_file";
                     next if ( -l "$dpdir/$patch_file" );
-                    unless (realpath("$dpdir/$patch_file") =~ m,^\Q$droot/,) {
-                        next;
-                    }
+                    next unless is_ancestor_of($droot, "$dpdir/$patch_file");
+
                     if (! -r "$dpdir/$patch_file") {
                         tag 'dpatch-index-references-non-existent-patch', $patch_file;
                         next;
@@ -120,7 +130,9 @@ sub run {
             tag "unneeded-build-dep-on-quilt";
         }
         #check for a debian/patches file:
-        if (! -r "$dpdir/series") {
+        if (-l "$dpdir/series" and not is_ancestor_of($droot, "$dpdir/series")) {
+            # skip
+        } elsif (! -r "$dpdir/series") {
             tag 'quilt-build-dep-but-no-series-file' unless $quilt_format;
         } else {
             if (open(IN, '<', "$dpdir/series")) {
@@ -146,9 +158,8 @@ sub run {
                 # Check each patch.
                 foreach my $patch_file (@patches) {
                     next if ( -l "$dpdir/$patch_file" );
-                    unless (realpath("$dpdir/$patch_file") =~ m,^\Q$droot\E/,) {
-                        next;
-                    }
+                    next unless is_ancestor_of($droot, "$dpdir/$patch_file");
+
                     if (! -r "$dpdir/$patch_file") {
                         tag 'quilt-series-references-non-existent-patch', $patch_file;
                         next;
@@ -174,22 +185,43 @@ sub run {
         if ($quilt_format) { # 3.0 (quilt) specific checks
             # Format 3.0 packages may generate a debian-changes-$version patch
             my $version = $info->field('version');
-            if (-f "$dpdir/debian-changes-$version" &&
-            ! -f "$droot/source/patch-header") {
-            tag 'format-3.0-but-debian-changes-patch';
+            my $versioned_patch = "$dpdir/debian-changes-$version";
+            my $patch_header = "$droot/source/patch-header";
+            my $ok = 1;
+            if (-l "$droot/source" or -l $patch_header) {
+                # possible issue
+                if (!is_ancenstor_of($droot, $patch_header)) {
+                    $ok = 0;
+                }
+            }
+            # $dpdir is known to be safe at this point, so only check
+            # the patch itself
+            if ($ok and -l $versioned_patch) {
+                if (!is_ancenstor_of($droot, $versioned_patch)) {
+                    $ok = 0;
+                }
+            }
+            if ($ok && -f $versioned_patch && ! -f $patch_header) {
+                tag 'format-3.0-but-debian-changes-patch';
             }
         }
     } else {
         if (-r "$dpdir/series" and
             -f "$dpdir/series") {
             # 3.0 (quilt) sources don't need quilt as dpkg-source will do the work
-            tag 'quilt-series-but-no-build-dep' unless $quilt_format;
+            if (! -l "$dpdir/series" or is_ancestor_of($droot, "$dpdir/series")) {
+                tag 'quilt-series-but-no-build-dep' unless $quilt_format;
+            }
         }
     }
 
     #----- look for README.source
     if ($uses_patch_system && ! $quilt_format && ! -f "$droot/README.source") {
-        tag "patch-system-but-no-source-readme";
+        if (! -l "$droot/README.source") {
+            # tag, unless README.source was a symlink (which could be a
+            # traversal attempt)
+            tag 'patch-system-but-no-source-readme';
+        }
     }
 
     #----- general cruft checking:
diff --git a/checks/po-debconf b/checks/po-debconf
index a21deac..89c0633 100644
--- a/checks/po-debconf
+++ b/checks/po-debconf
@@ -23,7 +23,7 @@ use strict;
 use warnings;
 
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail system_env);
+use Lintian::Util qw(fail is_ancestor_of system_env);
 
 sub run {
 
@@ -74,6 +74,10 @@ closedir(DEB);
 
 #TODO: check whether all templates are named in TEMPLATES.pot
 if ( $has_template ) {
+    if ( -l "$debfiles/po" and !is_ancestor_of($debfiles, "$debfiles/po")) {
+        # debian/po is an unsafe symlink - lets stop here.
+        return;
+    }
     if ( ! -d "$debfiles/po" ) {
         tag 'not-using-po-debconf';
         return 0;
@@ -90,7 +94,7 @@ for (@lang_templates) {
 
 my $missing_files = 0;
 
-if ( -f "$debfiles/po/POTFILES.in") {
+if ( -f "$debfiles/po/POTFILES.in" and ! -l "$debfiles/po/POTFILES.in") {
     open(POTFILES, '<', "$debfiles/po/POTFILES.in")
         or fail("Can't open debfiles/po/POTFILES.in.");
     while (<POTFILES>) {
@@ -109,7 +113,7 @@ if ( -f "$debfiles/po/POTFILES.in") {
     tag 'missing-potfiles-in';
     $missing_files = 1;
 }
-if (! -f "$debfiles/po/templates.pot") {
+if (! -f "$debfiles/po/templates.pot" and ! -l "$debfiles/po/templates.pot") {
     tag 'missing-templates-pot';
     $missing_files = 1;
 }
@@ -137,6 +141,8 @@ while (defined(my $file=readdir(DEBIAN))) {
         unless ($file =~ /^[a-z]{2,3}(_[A-Z]{2})?(?:\@[^\.]+)?\.po$/o);
     local ($/) = "\n\n";
     $_ = '';
+    # skip suspicious "files"
+    next if -l "$debfiles/po/$file" or ! -f "$debfiles/po/$file";
     open(PO, '<', "$debfiles/po/$file")
         or fail("Can't open debfiles/po/$file file.");
     while (<PO>) {
diff --git a/checks/rules b/checks/rules
index e02d7a6..e5f66eb 100644
--- a/checks/rules
+++ b/checks/rules
@@ -19,7 +19,7 @@ use warnings;
 
 use Lintian::Data;
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail);
+use Lintian::Util qw(fail is_ancestor_of);
 
 our $PYTHON_DEPEND = 'python | python-dev | python-all | python-all-dev';
 our $PYTHON3_DEPEND = 'python3 | python3-dev | python3-all | python3-all-dev';
@@ -118,7 +118,7 @@ my $rules = $info->debfiles('rules');
 # all the tests if we then can't read it.
 if (-l $rules) {
     tag 'debian-rules-is-symlink';
-    return 0 unless -f $rules;
+    return 0 unless -f $rules and is_ancestor_of($info->debfiles, $rules);
 }
 
 my $architecture = $info->field('architecture') || '';
diff --git a/checks/shared-libs b/checks/shared-libs
index f42b834..6eb63b1 100644
--- a/checks/shared-libs
+++ b/checks/shared-libs
@@ -289,7 +289,9 @@ for (keys %SONAME) {
 }
 @shlibs = grep { !$unversioned_shlibs{$_} } keys %SONAME;
 
-if ($#shlibs == -1) {
+if (-l $shlibsf) {
+    # control files are not symlinks, skip this part.
+} elsif ($#shlibs == -1) {
     # no shared libraries included in package, thus shlibs control file should
     # not be present
     if (-f $shlibsf) {
@@ -369,7 +371,9 @@ if ($#shlibs == -1) {
 
 # 5th step: check symbols control file.  Add back in the unversioned shared
 # libraries, since they can still have symbols files.
-if ($#shlibs == -1 and not %unversioned_shlibs) {
+if (-l $symbolsf) {
+    # control files are not symlinks, skip this part.
+} elsif ($#shlibs == -1 and not %unversioned_shlibs) {
     # no shared libraries included in package, thus symbols control file should
     # not be present
     if (-f $symbolsf) {
diff --git a/checks/watch-file b/checks/watch-file
index 7f1d26b..9618c31 100644
--- a/checks/watch-file
+++ b/checks/watch-file
@@ -27,7 +27,7 @@ use warnings;
 use Lintian::Collect;
 use Lintian::Tags qw(tag);
 
-use Lintian::Util qw(fail);
+use Lintian::Util qw(fail is_ancestor_of);
 
 sub run {
 
@@ -38,6 +38,10 @@ my $info = shift;
 my $template = 0;
 my $wfile = $info->debfiles('watch');
 
+if (-l $wfile) {
+    return unless is_ancestor_of($info->debfiles, $wfile);
+}
+
 if (! -f $wfile) {
     tag 'debian-watch-file-is-missing' unless ($info->native);
     return;
diff --git a/debian/changelog b/debian/changelog
index dd6fb51..ee918df 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,7 @@
 lintian (2.5.10.5) unstable; urgency=low
 
+  * checks/*:
+    + [NT] Avoid following unsafe symlinks.
   * checks/debconf:
     + [NT] Fix several path traversal issues that could leak
       information about the host system.

-- 
Debian package checker


Reply to: