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

[SCM] Debian package checker branch, master, updated. 2.5.11-269-g4fa9095



The following commit has been merged in the master branch:
commit 84d2dad5bdb3c23441128cb7c72c06d674ebcde3
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 9f02bb5..cf474e2 100644
--- a/checks/cruft
+++ b/checks/cruft
@@ -36,7 +36,7 @@ use constant BLOCKSIZE => 4096;
 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;
@@ -357,6 +357,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;
+    }
     $basename = basename $name;
 
     unless ($warned->{$name}) {
diff --git a/checks/debhelper b/checks/debhelper
index d3d54d0..931adee 100644
--- a/checks/debhelper
+++ b/checks/debhelper
@@ -26,7 +26,7 @@ use autodie qw(opendir closedir);
 use Lintian::Data;
 use Lintian::Relation;
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail slurp_entire_file strip strip);
+use Lintian::Util qw(fail is_ancestor_of slurp_entire_file strip strip);
 
 # If compat is less than or equal to this, then a missing version
 # for this level is only a pedantic issue.
@@ -85,6 +85,11 @@ my $seen_dh = 0;
 my $seen_python_helper = 0;
 my $seen_python3_helper = 0;
 
+if ( ! -f "$droot/rules" || !is_ancestor_of($droot, "$droot/rules")) {
+    # unsafe symlink
+    return;
+}
+
 open(RULES, '<', "$droot/rules") or fail("cannot read debian/rules: $!");
 
 while (<RULES>) {
@@ -296,6 +301,7 @@ for my $file (sort(readdir($dirfd))) {
     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 @@ for my $file (sort(readdir($dirfd))) {
         # 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 52432f4..788bf5e 100644
--- a/checks/infofiles
+++ b/checks/infofiles
@@ -76,6 +76,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 $fd = open_gz ($info->unpacked ($file));
         fail "open_gz $file: $!" unless defined $fd;
         local $_;
diff --git a/checks/init.d b/checks/init.d
index 2604bf2..bf91955 100644
--- a/checks/init.d
+++ b/checks/init.d
@@ -23,6 +23,7 @@ use strict;
 use warnings;
 use autodie qw(opendir closedir);
 
+use File::Basename qw(dirname);
 use List::MoreUtils qw(any none);
 
 use Lintian::Data;
@@ -231,6 +232,11 @@ sub check_init {
         if ($target =~ m,(?:\A|/)lib/init/upstart-job\z,) {
             return;
         }
+        if (!is_ancestor_of(dirname($initd_path), $initd_path)) {
+            # unsafe symlink, skip.  NB: dirname($initd_path) is safe
+            # because coll/init.d does sanity checking for us.
+            return;
+        }
     }
     open IN, '<', $initd_path
         or fail("cannot open init.d file $initd_file: $!");
diff --git a/checks/menu-format b/checks/menu-format
index 651f091..1f51982 100644
--- a/checks/menu-format
+++ b/checks/menu-format
@@ -42,7 +42,7 @@ use List::MoreUtils qw(any);
 
 use Lintian::Data;
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail);
+use Lintian::Util qw(fail is_ancestor_of);
 
 # This is a list of all tags that should be in every menu item.
 my @req_tags=qw(needs section title command);
@@ -252,6 +252,7 @@ foreach my $menufile (@menufiles) {
     $fullname = "usr/lib/menu/$basename" if $menufile =~ m,^\Q$mdir\E/lib/,;
 
     next if $basename eq 'README'; # README is a special case
+    next if !is_ancestor_of($mdir, $menufile);
 
     my $menufile_line ='';
     open (IN, '<', $menufile) or
@@ -537,6 +538,7 @@ sub verify_icon {
     }
 
     # 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");
@@ -544,6 +546,7 @@ sub verify_icon {
             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");
@@ -552,11 +555,19 @@ sub verify_icon {
         }
     }
 
-    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 c47609a..097e6e8 100644
--- a/checks/menus
+++ b/checks/menus
@@ -181,7 +181,8 @@ if ($docbase_file) {
     while (defined($dbfile = readdir($dirfd)) ) {
         my $dbpath = $info->lab_data_path ("doc-base/$dbfile");
         # don't try to parse executables, plus we already warned about it
-        next if -x $dbfile;
+        # - skip symlinks as well, unlikely to be used for real doc-base files.
+        next if -x $dbfile or -l $dbfile;
         check_doc_base_file ($dbfile, $dbpath, $pkg, $type, \%all_files, \%all_links,
                              $group);
     }
diff --git a/checks/patch-systems b/checks/patch-systems
index 90dcf98..4b2c8d9 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 strip);
+use Lintian::Util qw(fail is_ancestor_of strip);
 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;
@@ -117,7 +127,9 @@ sub run {
     if ($build_deps->implies('quilt') or $quilt_format) {
         $uses_patch_system++;
         #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")) {
@@ -144,9 +156,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;
@@ -172,22 +183,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" || 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 8ade5c0..dbcf872 100644
--- a/checks/po-debconf
+++ b/checks/po-debconf
@@ -24,7 +24,7 @@ use warnings;
 use autodie qw(opendir closedir);
 
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail system_env);
+use Lintian::Util qw(fail is_ancestor_of system_env);
 
 sub run {
 
@@ -82,6 +82,10 @@ closedir($dirfd);
 
 #TODO: check whether all templates are named in TEMPLATES.pot
 if ( $has_template ) {
+    if ( -l "$debfiles/po" and not 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;
@@ -98,7 +102,7 @@ for (@lang_templates) {
 
 my $missing_files = 0;
 
-if ( -f "$debfiles/po/POTFILES.in") {
+if ( -f "$debfiles/po/POTFILES.in" and not -l "$debfiles/po/POTFILES.in") {
     open(POTFILES, '<', "$debfiles/po/POTFILES.in")
         or fail("Can't open debfiles/po/POTFILES.in.");
     while (<POTFILES>) {
@@ -117,7 +121,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" && ! -l "$debfiles/po/templates.pot") {
     tag 'missing-templates-pot';
     $missing_files = 1;
 }
@@ -144,6 +148,8 @@ while (defined(my $file=readdir($po_dirfd))) {
         unless ($file =~ /^[a-z]{2,3}(_[A-Z]{2})?(?:\@[^\.]+)?\.po$/o);
     local ($/) = "\n\n";
     $_ = '';
+    # skip suspicious "files"
+    next if -l "$debfiles/po/$file" || ! -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 637dfa7..6a0bb19 100644
--- a/checks/rules
+++ b/checks/rules
@@ -21,7 +21,7 @@ use List::MoreUtils qw(any);
 
 use Lintian::Data;
 use Lintian::Tags qw(tag);
-use Lintian::Util qw(fail rstrip);
+use Lintian::Util qw(fail is_ancestor_of rstrip);
 
 our $PYTHON_DEPEND = 'python | python-dev | python-all | python-all-dev';
 our $PYTHON3_DEPEND = 'python3 | python3-dev | python3-all | python3-all-dev';
@@ -116,7 +116,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 660cf03..aaf5324 100644
--- a/checks/shared-libs
+++ b/checks/shared-libs
@@ -317,7 +317,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) {
@@ -399,7 +401,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 9943da2..26c0a5a 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 {
 
@@ -36,6 +36,10 @@ my (undef, undef, $info) = @_;
 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 7f29698..164f687 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -33,6 +33,8 @@ lintian (2.5.12) UNRELEASED; urgency=low
     + Removed:
       - unneeded-build-dep-on-quilt
 
+  * checks/*:
+    + [NT] Avoid following unsafe symlinks.
   * checks/binaries{,.desc}:
     + [NT] Accept libx32 as a bi-arch directory.
     + [NT] Correct reference policy reference.  Thanks to

-- 
Debian package checker


Reply to: