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

Bug#469924: lintian: suggestion: check for dynamic UIDs or GIDs in file ownership



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

Reply to: