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'});