--- Begin Message ---
- To: Debian Bug Tracking System <submit@bugs.debian.org>
- Subject: lintian: suggestion: check for dynamic UIDs or GIDs in file ownership
- From: Håkon Stordahl <haastord@online.no>
- Date: Fri, 07 Mar 2008 23:55:09 +0100
- Message-id: <87hcfi87qq.fsf@haakonst1.dyndns.org>
Package: lintian
Version: 1.23.46
Severity: wishlist
Tags: patch
Perhaps it could be useful to have a check in Lintian that examines
the UID and GID of the owner of files in binary packages. Though the
policy doesn't seem to explicitly forbid it, sections 9.2 and 10.9 at
least indicates that certain UIDs and GIDs (those which are
dynamically allocated) should not be used for the owner of files
shipped in Debian packages.
Some time ago I attempted a search for packages in the archive with
files of suspicious ownership, but not many packages turned out to
have such files. Admittedly, fakeroot, dh_fixperms or similar commands
seem to catch almost all potential problems. In unstable I could only
find strace (see bug #459255). Also, a few other packages from etch or
sarge have files with dynamically allocated UIDs and GIDs, but these
seem to have been removed or fixed in unstable.[1]
I've attached patches suggesting two alternative implementations of
such a check. I can't claim to have too much knowledge of Lintian
or Debian packaging, so there might be something here that I've not
thought of.
Unfortunately, tar doesn't seem to support printing of both the owner
ID, which will be required by the new check, and the owner name when
it is used to list the files in a package. So when generating the index
file for the binary package in the script unpack/unpack-binpkg-l1, the
list of files must be extracted from the package, using dpkg-deb and
tar, twice. Once where tar is invoked with the --numeric-owner option,
and once without it.
The first implementation merges the two lists of files from tar into a
single index file. However, I realized that, since this changes the
format of the index file, it might be unnecessarily complicated, as it
requires all check scripts which use to index file to be updated.
Thus, the second implementation, just outputs the list from tar, when
invoked with the --numeric-owner option, to a separate file, called
index-owner-id, instead.
The actual check is included in the files script, and it simply checks
that the owner UID and GID are global IDs, that is from the ranges
0-99, 64000-64999 or 65534. The check could possibly be narrowed
futher, for example to check that an ID from the range 0-99 actually
has been allocated. I've also added a simple test case for the check.
Finally, note there's the theoretic possibility that files in a
package might have owner IDs which doesn't properly correspond to the
owner names. For example, a file which is stored in the package with
owner names someuser:someuser could have owner IDs 0:0. The check
won't catch this case, but dpkg will install the files as
someuser:someuser (that is, if that user exists on the system). I
suppose the check also could be adapted to handle this, but I'm not
sure if that's worth the effort.
[1] These were jsboard-theme-diary-en, jsboard-theme-diary-ko
jsboard-theme-trash-en, jsboard-theme-trash-ko, jsboard-theme-wizz-ko,
nms-rand-link, nms-textcounter, pyblosxom and snd.
-- System Information:
Debian Release: lenny/sid
APT prefers unstable
APT policy: (990, 'unstable'), (500, 'testing'), (500, 'stable')
Architecture: i386 (i686)
Kernel: Linux 2.6.22-2-686 (SMP w/2 CPU cores)
Locale: LANG=en_US, LC_CTYPE=en_US (charmap=ISO-8859-1)
Shell: /bin/sh linked to /bin/bash
Versions of packages lintian depends on:
ii binutils 2.18.1~cvs20080103-1 The GNU assembler, linker and bina
ii diffstat 1.45-2 produces graph of changes introduc
ii dpkg-dev 1.14.16.6 package building tools for Debian
ii file 4.23-2 Determines file type using "magic"
ii gettext 0.17-2 GNU Internationalization utilities
ii intltool-debian 0.35.0+20060710.1 Help i18n of RFC822 compliant conf
ii libparse-debianchan 1.1.1-2 parse Debian changelogs and output
ii liburi-perl 1.35.dfsg.1-1 Manipulates and accesses URI strin
ii man-db 2.5.1-2 on-line manual pager
ii perl [libdigest-md5 5.8.8-12 Larry Wall's Practical Extraction
lintian recommends no packages.
-- no debconf information
diff -Naur a/checks/etcfiles b/checks/etcfiles
--- a/checks/etcfiles 2007-10-16 03:35:21.000000000 +0000
+++ b/checks/etcfiles 2008-03-07 15:26:29.000000000 +0000
@@ -48,7 +48,7 @@
while (<IN>) {
chop;
- my ($perm,$owner,$size,$date,$time,$file) = split(' ', $_, 6);
+ my ($perm,$owner_id,$owner,$size,$date,$time,$file) = split(' ', $_, 7);
my $link;
$file =~ s,^(\./),,;
diff -Naur a/checks/fields b/checks/fields
--- a/checks/fields 2008-02-08 18:26:26.000000000 +0000
+++ b/checks/fields 2008-03-07 15:26:29.000000000 +0000
@@ -406,7 +406,7 @@
local $_;
local $/ = "\n";
while (<IN>) {
- my ($mode, $file) = (split(' ', $_, 6))[0,5];
+ my ($mode, $file) = (split(' ', $_, 7))[0,6];
next unless $file;
$metapackage = 0 unless ($mode =~ /^d/ || $file =~ m%^\./usr/share/doc/%);
}
diff -Naur a/checks/files b/checks/files
--- a/checks/files 2008-02-10 18:24:05.000000000 +0000
+++ b/checks/files 2008-03-07 15:26:29.000000000 +0000
@@ -93,7 +93,7 @@
while (<IN>) {
chop;
- my ($perm,$owner,$size,$date,$time,$file) = split(' ', $_, 6);
+ my ($perm,$owner_id,$owner,$size,$date,$time,$file) = split(' ', $_, 7);
my $link;
my $operm;
@@ -128,6 +128,14 @@
tag "package-contains-ancient-file", "$file $date";
}
+ my ($owner_uid, $owner_gid) = split(/\//, $owner_id);
+ if (!($owner_uid < 100 || $owner_uid == 65534
+ || ($owner_uid >= 60000 && $owner_uid < 65000))
+ || !($owner_gid < 100 || $owner_gid == 65534
+ || ($owner_gid >= 60000 && $owner_gid < 65000))) {
+ tag "wrong-file-owner-uid-or-gid", $file, $owner_id;
+ }
+
# *.devhelp and *.devhelp2 files must be accessible from a directory in
# the devhelp search path: /usr/share/devhelp/books and
# /usr/share/gtk-doc/html. We therefore look for any links in one of
diff -Naur a/checks/files.desc b/checks/files.desc
--- a/checks/files.desc 2008-02-10 18:26:11.000000000 +0000
+++ b/checks/files.desc 2008-03-07 15:26:29.000000000 +0000
@@ -725,3 +725,13 @@
which is intended for detached debugging symbols, but the package name
does not end in "-dbg". Detached debugging symbols should be put into a
separate package, Priority: extra, with a package name ending in "-dbg".
+
+Tag: wrong-file-owner-uid-or-gid
+Type: error
+Info: The user or group ID of the owner of the file is invalid. The
+ owner user and group IDs must be in the set of globally allocated
+ IDs, because other IDs are dynamically allocated and might be used
+ for varying purposes on different systems, or are reserved. The set
+ of the allowed, globally allocated IDs consists of the ranges 0-99,
+ 64000-64999 and 65534.
+Ref: policy 9.2
diff -Naur a/checks/infofiles b/checks/infofiles
--- a/checks/infofiles 2008-02-03 21:26:36.000000000 +0000
+++ b/checks/infofiles 2008-03-07 15:26:29.000000000 +0000
@@ -66,7 +66,7 @@
while (<IN>) {
chop;
- my ($perm,$owner,$size,$date,$time,$file) = split(' ', $_, 6);
+ my ($perm,$owner_id,$owner,$size,$date,$time,$file) = split(' ', $_, 7);
my $link;
$file =~ s,^(\./),,;
diff -Naur a/checks/manpages b/checks/manpages
--- a/checks/manpages 2008-02-08 20:39:51.000000000 +0000
+++ b/checks/manpages 2008-03-07 15:26:29.000000000 +0000
@@ -63,7 +63,7 @@
while (<IN>) {
chop;
- my ($perm,$owner,$size,$date,$time,$file) = split(' ', $_, 6);
+ my ($perm,$owner_id,$owner,$size,$date,$time,$file) = split(' ', $_, 7);
my $link;
$file =~ s,^(\./),,;
diff -Naur a/checks/menu-format b/checks/menu-format
--- a/checks/menu-format 2008-02-21 02:41:48.000000000 +0000
+++ b/checks/menu-format 2008-03-07 15:26:29.000000000 +0000
@@ -400,7 +400,8 @@
open(IN, '<', "index") or fail("cannot open index file index: $!");
while (<IN>) {
chomp;
- my ($perm, $owner, $size, $date, $time, $file) = split(' ', $_, 6);
+ my ($perm, $owner_id, $owner, $size, $date, $time, $file)
+ = split(' ', $_, 7);
$file =~ s,^\./,/,;
$file =~ s/ link to .*//;
$file =~ s/ -> .*//;
diff -Naur a/checks/menus b/checks/menus
--- a/checks/menus 2008-03-03 23:58:16.000000000 +0000
+++ b/checks/menus 2008-03-07 15:26:29.000000000 +0000
@@ -92,7 +92,7 @@
open(IN, '<', "index") or fail("cannot open index file index: $!");
while (<IN>) {
chomp;
- my ($perm,$owner,$size,$date,$time,$file) = split(' ', $_, 6);
+ my ($perm,$owner_id,$owner,$size,$date,$time,$file) = split(' ', $_, 7);
$file =~ s,^(\./),,;
add_file_link_info ($file);
$file =~ s/ link to .*//;
diff -Naur a/checks/scripts b/checks/scripts
--- a/checks/scripts 2008-03-03 05:46:18.000000000 +0000
+++ b/checks/scripts 2008-03-07 15:26:29.000000000 +0000
@@ -198,8 +198,8 @@
chop;
s/ link to .*//;
my $is_suid = m/^-[rw-]*s/;
- $executable{(split(' ', $_, 6))[5]} = 1;
- $suid{(split(' ', $_, 6))[5]} = $is_suid;
+ $executable{(split(' ', $_, 7))[6]} = 1;
+ $suid{(split(' ', $_, 7))[6]} = $is_suid;
}
close(INDEX);
diff -Naur a/checks/shared-libs b/checks/shared-libs
--- a/checks/shared-libs 2008-03-03 21:50:22.000000000 +0000
+++ b/checks/shared-libs 2008-03-07 15:26:29.000000000 +0000
@@ -136,9 +136,9 @@
open(IN, '<', "index") or fail("cannot open index file index: $!");
while (<IN>) {
chop;
- @words = split(/\s+/o, $_, 6);
+ @words = split(/\s+/o, $_, 7);
my $perm = $words[0];
- my $cur_file = $words[5];
+ my $cur_file = $words[6];
$cur_file =~ s,^(\./),,;
$cur_file =~ s/ link to .*//;
diff -Naur a/collection/file-info b/collection/file-info
--- a/collection/file-info 2007-10-16 03:47:01.000000000 +0000
+++ b/collection/file-info 2008-03-07 15:26:29.000000000 +0000
@@ -42,7 +42,7 @@
or fail("cannot open index file: $!");
while (<INDEX>) {
chop;
- $_ = (split(" ", $_, 6))[5];
+ $_ = (split(" ", $_, 7))[6];
s/ link to .*//;
s/ -> .*//;
s/(\G|[^\\](?:\\\\)*)\\(\d{3})/"$1" . chr(oct $2)/ge;
diff -Naur a/collection/md5sums b/collection/md5sums
--- a/collection/md5sums 2007-10-16 03:47:11.000000000 +0000
+++ b/collection/md5sums 2008-03-07 15:26:29.000000000 +0000
@@ -43,7 +43,7 @@
while (<INDEX>) {
next unless m/^-/;
chop;
- $_ = (split(" ", $_, 6))[5];
+ $_ = (split(" ", $_, 7))[6];
s/ link to .*//;
s/\\(\d+)/chr(oct($1))/eg;
s/\\\\/\\/g;
diff -Naur a/collection/scripts b/collection/scripts
--- a/collection/scripts 2007-10-16 03:47:41.000000000 +0000
+++ b/collection/scripts 2008-03-07 15:26:29.000000000 +0000
@@ -35,7 +35,7 @@
# Extract the filename field from the tar-like file index.
# Note that the split is done with an explicit limit so that filenames
# with embedded spaces are handled correctly.
- $file = (split(' ', $_, 6))[5];
+ $file = (split(' ', $_, 7))[6];
$file =~ s/ link to .*//; # cut off info about hard links
# This used to call fail() instead of next. However, the check to
# see if all files in the index can be opened should be done elsewhere.
diff -Naur a/testset/filenames/debian/rules b/testset/filenames/debian/rules
--- a/testset/filenames/debian/rules 2008-03-04 00:16:24.000000000 +0000
+++ b/testset/filenames/debian/rules 2008-03-07 21:45:51.000000000 +0000
@@ -122,6 +122,15 @@
touch debian/tmp/usr/bin/bin/bad
chmod 755 debian/tmp/usr/bin/bin/bad
+ # Create some files with invalid ownership.
+ for owner in 100:0 0:2001 30001:65535 65535:65001; \
+ do \
+ i=$$(($${i:-0} + 1)); \
+ touch debian/tmp/usr/lib/filenames/wrong-owner-$$i; \
+ chmod 644 debian/tmp/usr/lib/filenames/wrong-owner-$$i; \
+ chown "$$owner" debian/tmp/usr/lib/filenames/wrong-owner-$$i; \
+ done
+
install -m 644 debian/changelog debian/tmp/usr/share/doc/filenames/Changes
gzip -9 debian/tmp/usr/share/doc/filenames/Changes
ln -s Changes.gz debian/tmp/usr/share/doc/filenames/changelog.gz
diff -Naur a/testset/tags.filenames b/testset/tags.filenames
--- a/testset/tags.filenames 2008-03-04 00:18:08.000000000 +0000
+++ b/testset/tags.filenames 2008-03-07 21:45:00.000000000 +0000
@@ -29,6 +29,10 @@
E: filenames: symlink-should-be-absolute usr/lib/filenames/symlink1wrong ../../../etc/symlink
E: filenames: use-of-compat-symlink usr/bin/X11/
E: filenames: use-of-compat-symlink usr/bin/X11/testxbin
+E: filenames: wrong-file-owner-uid-or-gid usr/lib/filenames/wrong-owner-1 100/0
+E: filenames: wrong-file-owner-uid-or-gid usr/lib/filenames/wrong-owner-2 0/2001
+E: filenames: wrong-file-owner-uid-or-gid usr/lib/filenames/wrong-owner-3 30001/65535
+E: filenames: wrong-file-owner-uid-or-gid usr/lib/filenames/wrong-owner-4 65535/65001
E: more-filename-games: no-copyright-file
I: filename-games: no-md5sums-control-file
I: filenames: file-in-usr-something-x11-without-pre-depends usr/include/X11/
diff -Naur a/unpack/unpack-binpkg-l1 b/unpack/unpack-binpkg-l1
--- a/unpack/unpack-binpkg-l1 2007-10-16 03:57:28.000000000 +0000
+++ b/unpack/unpack-binpkg-l1 2008-03-07 15:26:29.000000000 +0000
@@ -34,6 +34,7 @@
use lib "$ENV{'LINTIAN_ROOT'}/lib";
use Pipeline;
use Util;
+use FileHandle;
# stat $file
(my @stat = stat $file) or fail("$file: cannot stat: $!");
@@ -71,15 +72,50 @@
spawn("chmod", "-R", "u+rX,o-w", "$base_dir/control") == 0
or fail();
+# We want the owner IDs as well as the owner names of the files in the
+# package, but tar won't print both when listing the files, so tar
+# must be invoked twice, once with the --numeric-owner option and once
+# without it, and then the output must be merged.
+
# (replaces dpkg-deb -c)
# create index file for package
-pipeline((sub { exec "dpkg-deb", "--fsys-tarfile", $file }),
- (sub { exec "tar", "tfv", "-" }),
- (sub { exec "sed", "-e", "s/^h/-/" }),
- (sub { exec "sort", "-k", "6" }),
- "$base_dir/index") == 0
+my $INDEX1 = FileHandle->new;
+pipeline_open($INDEX1,
+ (sub { exec "dpkg-deb", "--fsys-tarfile", $file }),
+ (sub { exec "tar", "-tvf", "-" }),
+ (sub { exec "sed", "-e", "s/^h/-/" }),
+ (sub { exec "sort", "-k", "6" }))
or fail();
+# (replaces dpkg-deb -c)
+# create index file for package
+my $INDEX2 = FileHandle->new;
+pipeline_open($INDEX2,
+ (sub { exec "dpkg-deb", "--fsys-tarfile", $file }),
+ (sub { exec "tar", "--numeric-owner", "-tvf", "-" }),
+ (sub { exec "sed", "-e", "s/^h/-/" }),
+ (sub { exec "sort", "-k", "6" }))
+ or fail();
+
+# Merge the output from each tar invocation into a single index.
+my $INDEX = FileHandle->new;
+open($INDEX, ">", "$base_dir/index")
+ or fail("cannot open file $base_dir/index for writing: $!");
+while (my $line1 = <$INDEX1> and my $line2 = <$INDEX2>)
+{
+ my @fields1 = split(/ /, $line1, 3);
+ my @fields2 = split(/ /, $line2, 3);
+ print($INDEX "$fields1[0] ", sprintf("%-11s", $fields2[1]),
+ " $fields1[1] $fields1[2]");
+}
+
+close($INDEX1)
+ or fail("error while extracting package contents list: $file: $!");
+close($INDEX2)
+ or fail("error while extracting package contents list: $file: $!");
+close($INDEX)
+ or fail("failed to close $base_dir/index: $!");
+
# get package control information
my $data = (read_dpkg_control("$base_dir/control/control"))[0];
$data->{'source'} or ($data->{'source'} = $data->{'package'});
diff -Naur a/checks/files b/checks/files
--- a/checks/files 2008-02-10 18:24:05.000000000 +0000
+++ b/checks/files 2008-03-07 15:25:28.000000000 +0000
@@ -90,6 +90,8 @@
# Read package contents...
open(IN, '<', "index") or fail("cannot open index file index: $!");
+open(IN2, '<', "index-owner-id")
+ or fail("cannot open index file index-owner-id: $!");
while (<IN>) {
chop;
@@ -97,6 +99,12 @@
my $link;
my $operm;
+ my $line2 = <IN2>;
+ fail("cannot read index file index-owner-id") unless defined $line2;
+ chop($line2);
+ my ($owner_id, $file_chk) = (split(' ', $line2, 6))[1, 5];
+ fail("mismatching contents of index files") if $file ne $file_chk;
+
$file =~ s,^\./,,;
if ($file =~ s/ link to (.*)//) {
@@ -128,6 +136,14 @@
tag "package-contains-ancient-file", "$file $date";
}
+ my ($owner_uid, $owner_gid) = split(/\//, $owner_id);
+ if (!($owner_uid < 100 || $owner_uid == 65534
+ || ($owner_uid >= 60000 && $owner_uid < 65000))
+ || !($owner_gid < 100 || $owner_gid == 65534
+ || ($owner_gid >= 60000 && $owner_gid < 65000))) {
+ tag "wrong-file-owner-uid-or-gid", $file, $owner_id;
+ }
+
# *.devhelp and *.devhelp2 files must be accessible from a directory in
# the devhelp search path: /usr/share/devhelp/books and
# /usr/share/gtk-doc/html. We therefore look for any links in one of
@@ -854,6 +870,9 @@
}
close(IN);
+fail("mismatching contents of index files") if <IN2>;
+close(IN2);
+
#check for sect: games but nothing in /usr/games. Check for any binary to
#save ourselves from game-data false positives:
if ($pkg_section =~ m,games$,
diff -Naur a/checks/files.desc b/checks/files.desc
--- a/checks/files.desc 2008-02-10 18:26:11.000000000 +0000
+++ b/checks/files.desc 2008-03-07 15:25:28.000000000 +0000
@@ -725,3 +725,13 @@
which is intended for detached debugging symbols, but the package name
does not end in "-dbg". Detached debugging symbols should be put into a
separate package, Priority: extra, with a package name ending in "-dbg".
+
+Tag: wrong-file-owner-uid-or-gid
+Type: error
+Info: The user or group ID of the owner of the file is invalid. The
+ owner user and group IDs must be in the set of globally allocated
+ IDs, because other IDs are dynamically allocated and might be used
+ for varying purposes on different systems, or are reserved. The set
+ of the allowed, globally allocated IDs consists of the ranges 0-99,
+ 64000-64999 and 65534.
+Ref: policy 9.2
diff -Naur a/testset/filenames/debian/rules b/testset/filenames/debian/rules
--- a/testset/filenames/debian/rules 2008-03-04 00:16:24.000000000 +0000
+++ b/testset/filenames/debian/rules 2008-03-07 21:19:06.000000000 +0000
@@ -122,6 +122,15 @@
touch debian/tmp/usr/bin/bin/bad
chmod 755 debian/tmp/usr/bin/bin/bad
+ # Create some files with invalid ownership.
+ for owner in 100:0 0:2001 30001:65535 65535:65001; \
+ do \
+ i=$$(($${i:-0} + 1)); \
+ touch debian/tmp/usr/lib/filenames/wrong-owner-$$i; \
+ chmod 644 debian/tmp/usr/lib/filenames/wrong-owner-$$i; \
+ chown "$$owner" debian/tmp/usr/lib/filenames/wrong-owner-$$i; \
+ done
+
install -m 644 debian/changelog debian/tmp/usr/share/doc/filenames/Changes
gzip -9 debian/tmp/usr/share/doc/filenames/Changes
ln -s Changes.gz debian/tmp/usr/share/doc/filenames/changelog.gz
diff -Naur a/testset/tags.filenames b/testset/tags.filenames
--- a/testset/tags.filenames 2008-03-04 00:18:08.000000000 +0000
+++ b/testset/tags.filenames 2008-03-07 21:28:33.000000000 +0000
@@ -29,6 +29,10 @@
E: filenames: symlink-should-be-absolute usr/lib/filenames/symlink1wrong ../../../etc/symlink
E: filenames: use-of-compat-symlink usr/bin/X11/
E: filenames: use-of-compat-symlink usr/bin/X11/testxbin
+E: filenames: wrong-file-owner-uid-or-gid usr/lib/filenames/wrong-owner-1 100/0
+E: filenames: wrong-file-owner-uid-or-gid usr/lib/filenames/wrong-owner-2 0/2001
+E: filenames: wrong-file-owner-uid-or-gid usr/lib/filenames/wrong-owner-3 30001/65535
+E: filenames: wrong-file-owner-uid-or-gid usr/lib/filenames/wrong-owner-4 65535/65001
E: more-filename-games: no-copyright-file
I: filename-games: no-md5sums-control-file
I: filenames: file-in-usr-something-x11-without-pre-depends usr/include/X11/
diff -Naur a/unpack/unpack-binpkg-l1 b/unpack/unpack-binpkg-l1
--- a/unpack/unpack-binpkg-l1 2007-10-16 03:57:28.000000000 +0000
+++ b/unpack/unpack-binpkg-l1 2008-03-07 15:25:28.000000000 +0000
@@ -80,6 +80,15 @@
"$base_dir/index") == 0
or fail();
+# (replaces dpkg-deb -c)
+# create index file for package with owner IDs instead of names
+pipeline((sub { exec "dpkg-deb", "--fsys-tarfile", $file }),
+ (sub { exec "tar", "--numeric-owner", "-tvf", "-" }),
+ (sub { exec "sed", "-e", "s/^h/-/" }),
+ (sub { exec "sort", "-k", "6" }),
+ "$base_dir/index-owner-id") == 0
+ or fail();
+
# get package control information
my $data = (read_dpkg_control("$base_dir/control/control"))[0];
$data->{'source'} or ($data->{'source'} = $data->{'package'});
--- End Message ---