[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: