Package: netbase Version: 4.19 Severity: critical Tags: patch Justification: causes serious data loss
When I upgrade ipopd an unrelated (custom-made) entry in my inetd.conf is being deleted. The entry is pop3 stream tcp nowait nobody /usr/sbin/tcpd /usr/sbin/stunnel -c -r mail.argon.org:995 and it gets deleted when ipopd.postrm runs update-inetd --remove pop3s due to a bug in DebianNet. In tracing this down I found and fixed these problems in DebianNet.pm: - add_service(), remove_service(), and scan_entries() weren't properly anchoring the service search, so they could all match where they shouldn't have. - There's no need to set the umask before doing a chmod() because chmod() isn't affected by the umask. Additionally, leaving the process with a 0 umask is not safe. I fixed this by removing the umask() call. (A better fix would be to locally set a umask before calling open() and not do the chmod() at all, but I wanted to keep my changes minimal.) - The module causes some use of initialized value warnings. Here's a demonstration of all these problems, and my fix for them. After that is the patch. I'll also attach these files so you can get at them without having to cut/paste them out of the main part of this message. ------------------------------------------------------------------------------- $ cat do-test #!/usr/bin/perl -w use strict; use DebianNet (); print "using $INC{'DebianNet.pm'}\n"; $DebianNet::inetdcf = "test.conf"; $DebianNet::verbose = 1; my $base = "stream tcp nowait nobody file cmd"; # There's no need for DebianNet to set the umask because it doesn't affect # chmod. (Really it should be setting umask() before the open() instead # of doing the chmod(), but I wanted to keep my changes minimal.) umask 0777; print "various uninit warnings\n"; DebianNet::add_service "warn-add $base"; DebianNet::enable_service "warn-enable"; DebianNet::disable_service "warn-disable"; DebianNet::remove_service "warn-rm"; print "scan_entries() doesn't properly anchor search\n"; DebianNet::disable_service "test-scan"; print "add_service() doesn't properly anchor search\n"; DebianNet::add_service("test-add $base", "TEST"); print "remove_service() doesn't properly anchor search\n"; DebianNet::remove_service("arg"); $ cat test.conf.orig #:TEST: #<off># warn-enable stream tcp nowait nobody file cmd warn-disable stream tcp nowait nobody file cmd warn-rm stream tcp nowait nobody file cmd # This shouldn't be seen by add_service() because the #<off># isn't at # the start of the line. # x #<off># test-add stream tcp nowait nobody file cmd # This shouldn't be removed by remove_service("arg"). test-rm stream tcp nowait nobody file cmd arg # These shouldn't be found by scan_entries('test-scan'). test-scan-1 stream tcp nowait nobody file cmd arg test-scan-2 stream tcp nowait nobody file cmd arg $ cp test.conf.orig test.conf; \ perl do-test; \ diff -u test.conf.orig test.conf using /usr/share/perl5/DebianNet.pm various uninit warnings Use of uninitialized value in scalar chomp at /usr/share/perl5/DebianNet.pm line 25. Processing service `warn-add' ... added Use of uninitialized value in regexp compilation at /usr/share/perl5/DebianNet.pm line 199, <ICREAD> line 3. Processing service `warn-enable' ... enabled Use of uninitialized value in regexp compilation at /usr/share/perl5/DebianNet.pm line 236, <ICREAD> line 4. Use of uninitialized value in regexp compilation at /usr/share/perl5/DebianNet.pm line 174, <ICREAD> line 4. Processing service `warn-disable' ... disabled Use of uninitialized value in regexp compilation at /usr/share/perl5/DebianNet.pm line 236, <ICREAD> line 5. Removing line: `warn-rm stream tcp nowait nobody file cmd' scan_entries() doesn't properly anchor search Use of uninitialized value in regexp compilation at /usr/share/perl5/DebianNet.pm line 236, <ICREAD> line 17. Use of uninitialized value in regexp compilation at /usr/share/perl5/DebianNet.pm line 236, <ICREAD> line 18. WARNING!!!!!! test.conf contains multiple entries for the `test-scan' service. You're about to disable these entries. Do you want to continue? [n] Ok, I'll stop ... add_service() doesn't properly anchor search remove_service() doesn't properly anchor search Removing line: `# This shouldn't be removed by remove_service("arg").' Removing line: `test-rm stream tcp nowait nobody file cmd arg' Removing line: `test-scan-1 stream tcp nowait nobody file cmd arg' Removing line: `test-scan-2 stream tcp nowait nobody file cmd arg' --- test.conf.orig 2005-02-24 12:21:19.000000000 -0500 +++ test.conf 2005-02-24 12:39:51.000000000 -0500 @@ -1,19 +1,15 @@ #:TEST: -#<off># warn-enable stream tcp nowait nobody file cmd -warn-disable stream tcp nowait nobody file cmd -warn-rm stream tcp nowait nobody file cmd +warn-enable stream tcp nowait nobody file cmd +#<off># warn-disable stream tcp nowait nobody file cmd # This shouldn't be seen by add_service() because the #<off># isn't at # the start of the line. # x #<off># test-add stream tcp nowait nobody file cmd -# This shouldn't be removed by remove_service("arg"). -test-rm stream tcp nowait nobody file cmd arg # These shouldn't be found by scan_entries('test-scan'). -test-scan-1 stream tcp nowait nobody file cmd arg -test-scan-2 stream tcp nowait nobody file cmd arg +warn-add stream tcp nowait nobody file cmd $ cp test.conf.orig test.conf; \ perl -I. do-test; \ diff -u test.conf.orig test.conf using DebianNet.pm various uninit warnings Processing service `warn-add' ... added Processing service `warn-enable' ... enabled Processing service `warn-disable' ... disabled Removing line: `warn-rm stream tcp nowait nobody file cmd' scan_entries() doesn't properly anchor search add_service() doesn't properly anchor search Processing service `test-add' ... added remove_service() doesn't properly anchor search --- test.conf.orig 2005-02-24 12:21:19.000000000 -0500 +++ test.conf 2005-02-24 12:39:57.000000000 -0500 @@ -1,8 +1,8 @@ #:TEST: +test-add stream tcp nowait nobody file cmd -#<off># warn-enable stream tcp nowait nobody file cmd -warn-disable stream tcp nowait nobody file cmd -warn-rm stream tcp nowait nobody file cmd +warn-enable stream tcp nowait nobody file cmd +#<off># warn-disable stream tcp nowait nobody file cmd # This shouldn't be seen by add_service() because the #<off># isn't at # the start of the line. @@ -17,3 +17,4 @@ test-scan-1 stream tcp nowait nobody file cmd arg test-scan-2 stream tcp nowait nobody file cmd arg +warn-add stream tcp nowait nobody file cmd $ _ ------------------------------------------------------------------------------- --- /usr/share/perl5/DebianNet.pm 2004-10-31 11:29:34.000000000 -0500 +++ DebianNet.pm 2005-02-24 12:16:13.000000000 -0500 @@ -22,8 +22,12 @@ local($newentry, $group) = @_; local($service, $searchentry, @inetd, $inetdconf, $found, $success); unless (defined($newentry)) { return(-1) }; - chomp($newentry); chomp($group); - $group = "OTHER" unless (defined($group)); + chomp($newentry); + if (defined $group) { + chomp $group; + } else { + $group = "OTHER"; + } $group =~ tr/a-z/A-Z/; $newentry =~ s/\\t/\t/g; ($service = $newentry) =~ s/(\W*\w+)\s+.*/$1/; @@ -41,7 +45,7 @@ if (open(INETDCONF,"$inetdcf")) { @inetd=<INETDCONF>; close(INETDCONF); - if (grep(m/$sep$sservice\s+/,@inetd)) { + if (grep(m/^$sep$sservice\s+/,@inetd)) { &enable_service($sservice); } else { if (grep(m/^$sservice\s+/,@inetd)) { @@ -84,7 +88,7 @@ rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; - umask(000); chmod(0644, "$inetdcf"); + chmod(0644, "$inetdcf"); &wakeup_inetd; } @@ -131,7 +135,7 @@ open(ICREAD, "$inetdcf"); RLOOP: while(<ICREAD>) { chomp; - unless (/$service\b/) { + unless (/^$service\s+/) { print ICWRITE "$_\n"; } else { &printv("Removing line: \`$_'\n"); @@ -142,7 +146,7 @@ rename("$inetdcf.new", "$inetdcf") || die "Error installing new $inetdcf: $!\n"; - umask(000); chmod(0644, "$inetdcf"); + chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); @@ -151,6 +155,7 @@ sub disable_service { my($service, $pattern) = @_; unless (defined($service)) { return(-1) }; + unless (defined($pattern)) { $pattern = ''; } chomp($service); if ((&scan_entries("$service", $pattern) > 1) and (not defined($multi))) { @@ -182,7 +187,7 @@ rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; - umask(000); chmod(0644, "$inetdcf"); + chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); @@ -191,6 +196,7 @@ sub enable_service { my($service, $pattern) = @_; unless (defined($service)) { return(-1) }; + unless (defined($pattern)) { $pattern = ''; } chomp($service); open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n"; open(ICREAD, "$inetdcf"); @@ -207,7 +213,7 @@ rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; - umask(000); chmod(0644, "$inetdcf"); + chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); @@ -229,11 +235,12 @@ sub scan_entries { my ($service, $pattern) = @_; + unless (defined($pattern)) { $pattern = ''; } my $counter = 0; open(ICREAD, "$inetdcf"); SLOOP: while (<ICREAD>) { - $counter++ if (/^$service\b/ and /$pattern/); + $counter++ if (/^$service\s+/ and /$pattern/); } close(ICREAD); return($counter); ------------------------------------------------------------------------------- -- System Information: Debian Release: 3.1 APT prefers testing APT policy: (900, 'testing'), (700, 'unstable') Architecture: i386 (i686) Kernel: Linux 2.6.8-jones.1 Locale: LANG=en_US, LC_CTYPE=en_US (charmap=ISO-8859-1) Versions of packages netbase depends on: ii debconf 1.4.30.11 Debian configuration management sy ii ifupdown 0.6.4-4.10 High level tools to configure netw ii netkit-inetd 0.10-10 The Internet Superserver ii netkit-ping [ping] 0.10-10 The ping utility from netkit ii tcpd 7.6.dbs-6 Wietse Venema's TCP wrapper utilit -- debconf information excluded -- Roderick Schertler [EMAIL PROTECTED]
--- /usr/share/perl5/DebianNet.pm 2004-10-31 11:29:34.000000000 -0500 +++ DebianNet.pm 2005-02-24 12:16:13.000000000 -0500 @@ -22,8 +22,12 @@ local($newentry, $group) = @_; local($service, $searchentry, @inetd, $inetdconf, $found, $success); unless (defined($newentry)) { return(-1) }; - chomp($newentry); chomp($group); - $group = "OTHER" unless (defined($group)); + chomp($newentry); + if (defined $group) { + chomp $group; + } else { + $group = "OTHER"; + } $group =~ tr/a-z/A-Z/; $newentry =~ s/\\t/\t/g; ($service = $newentry) =~ s/(\W*\w+)\s+.*/$1/; @@ -41,7 +45,7 @@ if (open(INETDCONF,"$inetdcf")) { @inetd=<INETDCONF>; close(INETDCONF); - if (grep(m/$sep$sservice\s+/,@inetd)) { + if (grep(m/^$sep$sservice\s+/,@inetd)) { &enable_service($sservice); } else { if (grep(m/^$sservice\s+/,@inetd)) { @@ -84,7 +88,7 @@ rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; - umask(000); chmod(0644, "$inetdcf"); + chmod(0644, "$inetdcf"); &wakeup_inetd; } @@ -131,7 +135,7 @@ open(ICREAD, "$inetdcf"); RLOOP: while(<ICREAD>) { chomp; - unless (/$service\b/) { + unless (/^$service\s+/) { print ICWRITE "$_\n"; } else { &printv("Removing line: \`$_'\n"); @@ -142,7 +146,7 @@ rename("$inetdcf.new", "$inetdcf") || die "Error installing new $inetdcf: $!\n"; - umask(000); chmod(0644, "$inetdcf"); + chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); @@ -151,6 +155,7 @@ sub disable_service { my($service, $pattern) = @_; unless (defined($service)) { return(-1) }; + unless (defined($pattern)) { $pattern = ''; } chomp($service); if ((&scan_entries("$service", $pattern) > 1) and (not defined($multi))) { @@ -182,7 +187,7 @@ rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; - umask(000); chmod(0644, "$inetdcf"); + chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); @@ -191,6 +196,7 @@ sub enable_service { my($service, $pattern) = @_; unless (defined($service)) { return(-1) }; + unless (defined($pattern)) { $pattern = ''; } chomp($service); open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n"; open(ICREAD, "$inetdcf"); @@ -207,7 +213,7 @@ rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; - umask(000); chmod(0644, "$inetdcf"); + chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); @@ -229,11 +235,12 @@ sub scan_entries { my ($service, $pattern) = @_; + unless (defined($pattern)) { $pattern = ''; } my $counter = 0; open(ICREAD, "$inetdcf"); SLOOP: while (<ICREAD>) { - $counter++ if (/^$service\b/ and /$pattern/); + $counter++ if (/^$service\s+/ and /$pattern/); } close(ICREAD); return($counter);
do-test
Description: Perl program
#:TEST: #<off># warn-enable stream tcp nowait nobody file cmd warn-disable stream tcp nowait nobody file cmd warn-rm stream tcp nowait nobody file cmd # This shouldn't be seen by add_service() because the #<off># isn't at # the start of the line. # x #<off># test-add stream tcp nowait nobody file cmd # This shouldn't be removed by remove_service("arg"). test-rm stream tcp nowait nobody file cmd arg # These shouldn't be found by scan_entries('test-scan'). test-scan-1 stream tcp nowait nobody file cmd arg test-scan-2 stream tcp nowait nobody file cmd arg
# DebianNet.pm: a perl module to add entries to the /etc/inetd.conf file # # Copyright (C) 1995, 1996 Peter Tobias <[EMAIL PROTECTED]> # Ian Jackson <[EMAIL PROTECTED]> # # # DebianNet::add_service($newentry, $group); # DebianNet::disable_service($service, $pattern); # DebianNet::enable_service($service, $pattern); # DebianNet::remove_service($entry); # package DebianNet; require 5.000; $inetdcf="/etc/inetd.conf"; $sep = "#<off># "; $version = "1.11"; sub add_service { local($newentry, $group) = @_; local($service, $searchentry, @inetd, $inetdconf, $found, $success); unless (defined($newentry)) { return(-1) }; chomp($newentry); if (defined $group) { chomp $group; } else { $group = "OTHER"; } $group =~ tr/a-z/A-Z/; $newentry =~ s/\\t/\t/g; ($service = $newentry) =~ s/(\W*\w+)\s+.*/$1/; ($sservice = $service) =~ s/^#([A-Za-z].*)/$1/; ($searchentry = $newentry) =~ s/^$sep//; $searchentry =~ s/^#([A-Za-z].*)/$1/; # strip parameter from entry (e.g. -s /tftpboot) # example: service dgram udp wait root /tcpd /prg -s /tftpboot"; $searchentry =~ s/^(\w\S+\W+\w+\W+\w\S+\W+\w\S+\W+\w\S+\W+\S+\W+\S+).*/$1/; $searchentry =~ s/[ \t]+/ /g; $searchentry =~ s/ /\\s+/g; $searchentry =~ [EMAIL PROTECTED]/\S+\\s\+/[EMAIL PROTECTED]@g; if (open(INETDCONF,"$inetdcf")) { @inetd=<INETDCONF>; close(INETDCONF); if (grep(m/^$sep$sservice\s+/,@inetd)) { &enable_service($sservice); } else { if (grep(m/^$sservice\s+/,@inetd)) { if (grep(m/^$sservice\s+/,@inetd) > 1) { &inetde("There are several entries for $sservice in $inetdcf\n"); } elsif (!grep(m:^#?.*$searchentry.*:, @inetd)) { print"\nTrying to add the following entry:\n\n $newentry\n\n"; &inetde("There is already an entry for $sservice in $inetdcf, but I don't recognise it. Here is what it looks like:\n ".join(' ',grep(m/^$sservice\s+/,@inetd))); } } elsif (grep(m/^#\s*$sservice\s+/, @inetd) >= 1 or (($service =~ s/^#//) and grep(m/^$service\s+/, @inetd)>=1)) { &printv("Processing service \`$service' ... not changed\n"); } else { &printv("Processing service \`$sservice' ... added\n"); $inetdconf=1; } } if ($inetdconf) { open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n"; open(ICREAD, "$inetdcf"); while(<ICREAD>) { chomp; if (/^#:$group:/) { $found = 1; }; if ($found and !(/[a-zA-Z#]/)) { print (ICWRITE "$newentry\n") || die "Error writing new $inetdcf: $!\n"; $found = 0; $success = 1; } print ICWRITE "$_\n"; } close(ICREAD); unless ($success) { print (ICWRITE "$newentry\n") || die "Error writing new $inetdcf: $!\n"; } close(ICWRITE) || die "Error closing new inetd.conf: $!\n"; rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; chmod(0644, "$inetdcf"); &wakeup_inetd; } } sub inetde { my($response); do { print @_, "\nDo you want to ignore this potential problem and continue, or would you rather not do so now ? Continue? (n/y) "; $!=0; defined($response=<STDIN>) || die "netconfig: EOF/error on stdin: $!\n"; } while ($response !~ m/^\s*[yn]?\s*$/i); return(1) if($response =~ m/y/i); exit(1); } return(1); } sub remove_service { my($service) = @_; unless(defined($service)) { return(-1) }; chomp($service); if($service eq "") { print "DebianNet::remove_service called with empty argument\n"; return(-1); } if ((&scan_entries("$service") > 1) and (not defined($multi))) { print "\nWARNING!!!!!! $inetdcf contains multiple entries for \n"; print "the \`$service' service. You're about to remove these entries.\n"; print "Do you want to continue? [n] "; if (<STDIN> =~ /^[^y]/i) { print "\nOk, I'll stop ...\n"; return(1); } else { if ($want_continue == 0) { print "\nOk, I'll continue ...\n"; } } } open(ICWRITE, ">$inetdcf.new") || die "Error creating $inetdcf.new"; open(ICREAD, "$inetdcf"); RLOOP: while(<ICREAD>) { chomp; unless (/^$service\s+/) { print ICWRITE "$_\n"; } else { &printv("Removing line: \`$_'\n"); } } close(ICREAD); close(ICWRITE); rename("$inetdcf.new", "$inetdcf") || die "Error installing new $inetdcf: $!\n"; chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); } sub disable_service { my($service, $pattern) = @_; unless (defined($service)) { return(-1) }; unless (defined($pattern)) { $pattern = ''; } chomp($service); if ((&scan_entries("$service", $pattern) > 1) and (not defined($multi))) { print "\nWARNING!!!!!! $inetdcf contains multiple entries for \n"; print "the \`$service' service. You're about to disable these entries.\n"; print "Do you want to continue? [n] "; if (<STDIN> =~ /^[^y]/i) { print "\nOk, I'll stop ...\n"; return(1); } else { if ($want_continue == 0) { print "\nOk, I'll continue ...\n"; } } } open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n"; open(ICREAD, "$inetdcf"); DLOOP: while(<ICREAD>) { chomp; if (/^$service\s+\w+\s+/ and /$pattern/) { &printv("Processing service \`$service' ... disabled\n"); $_ =~ s/^(.+)$/$sep$1/; } print ICWRITE "$_\n"; } close(ICREAD); close(ICWRITE) || die "Error closing new inetd.conf: $!\n"; rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); } sub enable_service { my($service, $pattern) = @_; unless (defined($service)) { return(-1) }; unless (defined($pattern)) { $pattern = ''; } chomp($service); open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n"; open(ICREAD, "$inetdcf"); while(<ICREAD>) { chomp; if (/^$sep$service\s+\w+\s+/ and /$pattern/) { &printv("Processing service \`$service' ... enabled\n"); $_ =~ s/^$sep//; } print ICWRITE "$_\n"; } close(ICREAD); close(ICWRITE) || die "Error closing new inetd.conf: $!\n"; rename("$inetdcf.new","$inetdcf") || die "Error installing new $inetdcf: $!\n"; chmod(0644, "$inetdcf"); &wakeup_inetd; return(1); } sub wakeup_inetd { my($pid); if (open(P,"/var/run/inetd.pid")) { $pid=<P>; if (open(C,sprintf("/proc/%d/stat",$pid))) { $_=<C>; if (m/^\d+ \(inetd\)/) { kill(1,$pid); } close(C); } close(P); } return(1); } sub scan_entries { my ($service, $pattern) = @_; unless (defined($pattern)) { $pattern = ''; } my $counter = 0; open(ICREAD, "$inetdcf"); SLOOP: while (<ICREAD>) { $counter++ if (/^$service\s+/ and /$pattern/); } close(ICREAD); return($counter); } sub printv { print @_ if (defined($verbose)); } 1;