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

Re: Fixes for RT 3.x issue CVE-2011-0009 in testing: preapproval requested



On Thu, Jan 20, 2011 at 08:39:12AM +0000, Dominic Hargreaves wrote:

> I attach the full interdiff for review. Please could you let me
> know whether it would be okay to go ahead with this upload to unstable,
> or whether a targetted upload covering just the security fix will be
> required,

No, really, attached now :)

Dominic.

-- 
Dominic Hargreaves | http://www.larted.org.uk/~dom/
PGP key 5178E2A5 from the.earth.li (keyserver,web,email)
diff -u request-tracker3.8-3.8.8/debian/rules request-tracker3.8-3.8.8/debian/rules
--- request-tracker3.8-3.8.8/debian/rules
+++ request-tracker3.8-3.8.8/debian/rules
@@ -133,6 +133,8 @@
 
 	install -m 644 debian/lintian-overrides $(RT3_PKG)/usr/share/lintian/overrides/$(RT3)
 
+	install -m 755 debian/scripts/vulnerable-passwords $(RT3_PKG)/usr/sbin/rt-vulnerable-passwords-3.8
+
 	# dh_link already made the pgsql and sqlite links
 	install -m 755 debian/scripts/dbconfig-install $(RT3_PKG)/usr/share/dbconfig-common/scripts/request-tracker3.8/install/mysql
 	install -m 755 debian/scripts/dbconfig-upgrade-3.8.2 $(RT3_PKG)/usr/share/dbconfig-common/scripts/request-tracker3.8/upgrade/mysql/3.8.2
diff -u request-tracker3.8-3.8.8/debian/postinst request-tracker3.8-3.8.8/debian/postinst
--- request-tracker3.8-3.8.8/debian/postinst
+++ request-tracker3.8-3.8.8/debian/postinst
@@ -99,7 +99,7 @@
     then
         ucfr request-tracker3.8 /etc/request-tracker3.8/RT_SiteConfig.d/50-debconf
         ucfr request-tracker3.8 /etc/request-tracker3.8/RT_SiteConfig.pm
-        ucfr request-tracker3.8 /etc/cron.d/request-tracker3.8
+        ucfr request-tracker3.8 /etc/cron.d/request-tracker38
         # this should probably be registered by dbconfig-generate-include,
         # but ucfr is idempotent so it doesn't hurt here anyway.
         ucfr request-tracker3.8 /etc/request-tracker3.8/RT_SiteConfig.d/51-dbconfig-common
@@ -132,10 +132,28 @@
 EOF
     fi
     mkdir -p /etc/cron.d
-    ucf --debconf-ok $tfile /etc/cron.d/request-tracker3.8
+    if [ -f /etc/cron.d/request-tracker3.8 ]; then
+        mv /etc/cron.d/request-tracker3.8 /etc/cron.d/request-tracker38
+    fi
+    ucf --debconf-ok $tfile /etc/cron.d/request-tracker38
     rm $tfile
 }
 
+fix_vulnerable_passwords() {
+    if [ "$1" = "configure" ] && [ -n "$2" ] && \
+        dpkg --compare-versions "$2" lt 3.8.8-7
+    then
+        if su -c "/usr/sbin/rt-vulnerable-passwords-3.8 --fix" www-data; then
+            echo "rt-vulnerable-passwords-3.8 invoked successfully on upgrade"
+        else
+            echo "rt-vulnerable-passwords-3.8 exited with an error but the"
+            echo "package post-installation will continue. We recommend that"
+            echo "you check the above error and take corrective action to"
+            echo "ensure the privacy of your user's passwords."
+        fi
+    fi
+}
+
 # The actual work starts here
 . /usr/share/debconf/confmodule
 
@@ -155,6 +173,7 @@
 maybe_handle_permissions
 setup_cronjobs
 ucf_register
+fix_vulnerable_passwords $@
 
 case "$1" in
     configure)
diff -u request-tracker3.8-3.8.8/debian/changelog request-tracker3.8-3.8.8/debian/changelog
--- request-tracker3.8-3.8.8/debian/changelog
+++ request-tracker3.8-3.8.8/debian/changelog
@@ -1,3 +1,18 @@
+request-tracker3.8 (3.8.8-7~test.3) unstable; urgency=high
+
+  * Correct name of file in cron.d to one which will be run by cron
+    (Closes: #607209)
+  * Apply patch from upstream reducing the severity of the
+    RTAddressRegexp warning message to "debug", to avoid the cron jobs
+    generating noise
+  * Remove completely misleading documentation from NOTES.Debian
+    relating to migrating between SQLite and other databases
+    (Closes: #608481)
+  * Security fix: support salted passwords in database and upgrade
+    unsalted passwords (CVE-2011-0009)
+
+ -- Dominic Hargreaves <dom@earth.li>  Sun, 16 Jan 2011 18:14:52 +0000
+
 request-tracker3.8 (3.8.8-6) unstable; urgency=low
 
   * Make sure /etc/cron.d exists in postinst before installing cronjob,
diff -u request-tracker3.8-3.8.8/debian/NOTES.Debian request-tracker3.8-3.8.8/debian/NOTES.Debian
--- request-tracker3.8-3.8.8/debian/NOTES.Debian
+++ request-tracker3.8-3.8.8/debian/NOTES.Debian
@@ -46,36 +46,6 @@
 We strongly recommend that you use MySQL or PostgreSQL for production
 use, by installing rt3.8-db-mysql or rt3.8-db-postgresql.
 
-DATABASE CONVERSION
--------------------
-
-An existing SQLite installation can be converted to MySQL or PostgreSQL
-by either
-
-  * installing rt3.8-db-mysql or rt3.8-db-postgresql and then running
-    'dpkg-reconfigure request-tracker3.8' and picking the new choices, or
-  * editing /etc/request-tracker3.8/RT_SiteConfig* and setting up the
-    new database with 'rt-setup-database --action init' manually. 
-
-In either case, if you want to keep existing database content, you
-should run
-
-    rt-dump-database > dumpfile
-
-first. After the new database is configured, just run
-
-    rt-setup-database --action insert --datafile dumpfile
-
-Note that you will need to reset the RT root password, it is not included
-in the dump.
-
-The reason the root password is not included is that rt-dump-database
-does not dump predefined system objects by default. If you have modified
-some of these, you may want to use 'rt-setup-database 0' to get the system
-objects too. However, be aware that 'rt-setup-database --action insert' on
-an already initialized database will then duplicate the objects.
-Suggestions on the best way to handle this situation are welcome. 
-
 DATABASE CREATION AND SCHEMA INSTALLATION
 -----------------------------------------
 
diff -u request-tracker3.8-3.8.8/debian/control request-tracker3.8-3.8.8/debian/control
--- request-tracker3.8-3.8.8/debian/control
+++ request-tracker3.8-3.8.8/debian/control
@@ -40,7 +40,7 @@
  libjs-scriptaculous, libjs-prototype, libipc-run-safehandles-perl,
  perl (>= 5.10.0) | libencode-perl (>= 2.21),
  perl (>= 5.12.0) | libcgi-pm-perl (>= 3.38), libcgi-fast-perl,
- libfcgi-procmanager-perl,
+ libfcgi-procmanager-perl, libdigest-sha-perl,
  ${misc:Depends}
 Recommends: speedy-cgi-perl, libdatetime-locale-perl, libdatetime-perl, cron-daemon
 Suggests: rt3.8-rtfm
diff -u request-tracker3.8-3.8.8/debian/patches/00list request-tracker3.8-3.8.8/debian/patches/00list
--- request-tracker3.8-3.8.8/debian/patches/00list
+++ request-tracker3.8-3.8.8/debian/patches/00list
@@ -13,0 +14,2 @@
+60_rtaddressregexp_not_error
+74_salted_passwords
only in patch2:
unchanged:
--- request-tracker3.8-3.8.8.orig/debian/scripts/vulnerable-passwords
+++ request-tracker3.8-3.8.8/debian/scripts/vulnerable-passwords
@@ -0,0 +1,89 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+BEGIN {
+    use lib qw(/usr/share/request-tracker3.8/lib);
+    use RT;
+    RT::LoadConfig;
+    RT::Init;
+}
+
+$| = 1;
+
+use Getopt::Long;
+my $fix;
+GetOptions("fix!" => \$fix);
+
+use RT::Users;
+my $users = RT::Users->new( $RT::SystemUser );
+$users->Limit(
+    FIELD => 'Password',
+    OPERATOR => 'IS NOT',
+    VALUE => 'NULL',
+    ENTRYAGGREGATOR => 'AND',
+);
+$users->Limit(
+    FIELD => 'Password',
+    OPERATOR => '!=',
+    VALUE => '*NO-PASSWORD*',
+    ENTRYAGGREGATOR => 'AND',
+);
+$users->Limit(
+    FIELD => 'Password',
+    OPERATOR => 'NOT STARTSWITH',
+    VALUE => '!',
+    ENTRYAGGREGATOR => 'AND',
+);
+push @{$users->{'restrictions'}{ "main.Password" }},
+    "AND LENGTH(main.Password) < 40";
+
+my $count = $users->Count;
+if ($count == 0) {
+    print "No users with unsalted or weak cryptography found.\n";
+    exit 0;
+}
+
+if ($fix) {
+    print "Upgrading $count users...\n";
+    while (my $u = $users->Next) {
+        my $stored = $u->__Value("Password");
+        my $raw;
+        if (length $stored == 32) {
+            $raw = pack("H*",$stored);
+        } elsif (length $stored == 22) {
+            $raw = MIME::Base64::decode_base64($stored);
+        } elsif (length $stored == 13) {
+            printf "%20s => Old crypt() format, cannot upgrade\n", $u->Name;
+        } else {
+            printf "%20s => Unknown password format!\n", $u->Name;
+        }
+        next unless $raw;
+
+        my $salt = pack("C4",map{int rand(256)} 1..4);
+        my $sha = Digest::SHA::sha256(
+            $salt . $raw
+        );
+        $u->_Set(
+            Field => "Password",
+            Value => MIME::Base64::encode_base64(
+                $salt . substr($sha,0,26)),
+        );
+    }
+    print "Done.\n";
+    exit 0;
+} else {
+    if ($count < 20) {
+        print "$count users found with unsalted or weak-cryptography passwords:\n";
+        print "      Id | Name\n", "-"x9, "+", "-"x9, "\n";
+        while (my $u = $users->Next) {
+            printf "%8d | %s\n", $u->Id, $u->Name;
+        }
+    } else {
+        print "$count users found with unsalted or weak-cryptography passwords\n";
+    }
+
+    print "\n", "Run again with --fix to upgrade.\n";
+    exit 1;
+}
only in patch2:
unchanged:
--- request-tracker3.8-3.8.8.orig/debian/patches/60_rtaddressregexp_not_error.dpatch
+++ request-tracker3.8-3.8.8/debian/patches/60_rtaddressregexp_not_error.dpatch
@@ -0,0 +1,25 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 60_rtaddressregexp_not_error.dpatch by Jesse Vincent <jesse@bestpractical.com>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: commit 4409d6eab940357c04cb4e4c0b57ee82f1b0560d
+## DP: Author: Jesse Vincent <jesse@bestpractical.com>
+## DP: Date:   Wed Jul 14 20:21:40 2010 +0200
+## DP: 
+## DP:     Drop the RTAddressRegexp down to a debug message so it stops complaining
+## DP:     on any script being run on every RT instance on the planet.
+
+@DPATCH@
+diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
+index 051117a..7e7a659 100644
+--- a/lib/RT/Config.pm
++++ b/lib/RT/Config.pm
+@@ -354,7 +354,7 @@ our %META = (
+             my $value = $self->Get('RTAddressRegexp');
+             return if $value;
+ 
+-            $RT::Logger->error(
++            $RT::Logger->debug(
+                 'The RTAddressRegexp option is not set in the config.'
+                 .' Not setting this option results in additional SQL queries to'
+                 .' check whether each address belongs to RT or not.'
only in patch2:
unchanged:
--- request-tracker3.8-3.8.8.orig/debian/patches/74_salted_passwords.dpatch
+++ request-tracker3.8-3.8.8/debian/patches/74_salted_passwords.dpatch
@@ -0,0 +1,98 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 74_salted_passwords.dpatch
+##
+## DP: Support and upgrade to salted passwords (security fix)
+diff -urN rt-3.8.8.orig//lib/RT/User_Overlay.pm rt-3.8.8/lib/RT/User_Overlay.pm
+--- rt-3.8.8.orig//lib/RT/User_Overlay.pm	2010-05-05 21:09:21.000000000 +0100
++++ rt-3.8.8/lib/RT/User_Overlay.pm	2011-01-16 18:33:45.000000000 +0000
+@@ -74,6 +74,7 @@
+ use RT::ACE;
+ use RT::Interface::Email;
+ use Encode;
++use Digest::SHA;
+ 
+ sub _OverlayAccessible {
+     {
+@@ -996,12 +997,19 @@
+ 
+ sub _GeneratePassword {
+     my $self = shift;
+-    my $password = shift;
++    my ($password, $salt) = @_;
+ 
+-    my $md5 = Digest::MD5->new();
+-    $md5->add(encode_utf8($password));
+-    return ($md5->hexdigest);
++    # Generate a random 4-byte salt
++    $salt ||= pack("C4",map{int rand(256)} 1..4);
+ 
++    # Encode the salt, and a truncated SHA256 of the MD5 of the
++    # password.  The additional, un-necessary level of MD5 allows for
++    # transparent upgrading to this scheme, from the previous unsalted
++    # MD5 one.
++    return MIME::Base64::encode_base64(
++        $salt . substr(Digest::SHA::sha256($salt . Digest::MD5::md5($password)),0,26),
++        "" # No newline
++    );
+ }
+ 
+ =head3 _GeneratePasswordBase64 PASSWORD
+@@ -1047,9 +1055,7 @@
+     my $self  = shift;
+     my $value = shift;
+ 
+-    #TODO there isn't any apparent way to legitimately ACL this
+-
+-    # RT does not allow null passwords 
++    # RT does not allow null passwords
+     if ( ( !defined($value) ) or ( $value eq '' ) ) {
+         return (undef);
+     }
+@@ -1064,23 +1070,32 @@
+         return(undef);
+      }
+ 
+-    # generate an md5 password 
+-    if ($self->_GeneratePassword($value) eq $self->__Value('Password')) {
+-        return(1);
+-    }
+-
+-    #  if it's a historical password we say ok.
+-    if ($self->__Value('Password') eq crypt(encode_utf8($value), $self->__Value('Password'))
+-        or $self->_GeneratePasswordBase64($value) eq $self->__Value('Password'))
+-    {
+-        # ...but upgrade the legacy password inplace.
+-        $self->SUPER::SetPassword( $self->_GeneratePassword($value) );
+-        return(1);
++    my $stored = $self->__Value('Password');
++    if (length $stored == 40) {
++        # The truncated SHA256(salt,MD5(passwd)) form from 2010/12 is 40 characters long
++        my $hash = MIME::Base64::decode_base64($stored);
++        # The first 4 bytes are the salt, the rest is substr(SHA256,0,26)
++        my $salt = substr($hash, 0, 4, "");
++        return substr(Digest::SHA::sha256($salt . Digest::MD5::md5($value)), 0, 26) eq $hash;
++    } elsif (length $stored == 32) {
++        # Hex nonsalted-md5
++        return 0 unless Digest::MD5::md5_hex(Encode::encode_utf8($value)) eq $stored;
++    } elsif (length $stored == 22) {
++        # Base64 nonsalted-md5
++        return 0 unless Digest::MD5::md5_base64(Encode::encode_utf8($value)) eq $stored;
++    } elsif (length $stored == 13) {
++        # crypt() output
++        return 0 unless crypt(Encode::encode_utf8($value), $stored) eq $stored;
++    } else {
++        $RT::Logger->warn("Unknown password form");
++        return 0;
+     }
+ 
+-    # no password check has succeeded. get out
+-
+-    return (undef);
++    # We got here by validating successfully, but with a legacy
++    # password form.  Update to the most recent form.
++    my $obj = $self->isa("RT::CurrentUser") ? $self->UserObj : $self;
++    $obj->_Set(Field => 'Password', Value =>  $self->_GeneratePassword($value) );
++    return 1;
+ }
+ 
+ sub CurrentUserRequireToSetPassword {

Reply to: