[rt3/el5/master] CVE-2011-0009
Xavier Bachelot
xavierb at fedoraproject.org
Wed Jan 26 23:21:42 UTC 2011
commit d9fc10e06f0d79ec6315ed8a21e4266a1311d798
Author: Xavier Bachelot <xavier at bachelot.org>
Date: Wed Jan 26 19:39:58 2011 +0100
CVE-2011-0009
rt3-3.6.10-salted_passwords.patch | 254 +++++++++++++++++++++++++++++++++++++
rt3.spec | 8 +-
2 files changed, 261 insertions(+), 1 deletions(-)
---
diff --git a/rt3-3.6.10-salted_passwords.patch b/rt3-3.6.10-salted_passwords.patch
new file mode 100644
index 0000000..f826269
--- /dev/null
+++ b/rt3-3.6.10-salted_passwords.patch
@@ -0,0 +1,254 @@
+diff -Naur rt-3.6.10.orig/configure rt-3.6.10/configure
+--- rt-3.6.10.orig/configure 2009-11-30 19:47:53.000000000 +0100
++++ rt-3.6.10/configure 2011-01-27 00:15:25.000000000 +0100
+@@ -2646,6 +2646,8 @@
+
+ ac_config_files="$ac_config_files Makefile etc/RT_Config.pm lib/RT.pm bin/mason_handler.svc bin/webmux.pl"
+
++ac_config_files="$ac_config_files etc/upgrade/vulnerable-passwords"
++
+ cat >confcache <<\_ACEOF
+ # This file is a shell script that caches the results of configure
+ # tests run on this system so they can be shared between configure
+@@ -3356,6 +3358,7 @@
+ "lib/RT.pm") CONFIG_FILES="$CONFIG_FILES lib/RT.pm" ;;
+ "bin/mason_handler.svc") CONFIG_FILES="$CONFIG_FILES bin/mason_handler.svc" ;;
+ "bin/webmux.pl") CONFIG_FILES="$CONFIG_FILES bin/webmux.pl" ;;
++ "etc/upgrade/vulnerable-passwords") CONFIG_FILES="$CONFIG_FILES etc/upgrade/vulnerable-passwords" ;;
+
+ *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+@@ -3783,7 +3786,8 @@
+ ;;
+ "bin/rt":F) chmod ug+x $ac_file
+ ;;
+-
++ "etc/upgrade/vulnerable-passwords":F) chmod ug+x $ac_file
++ ;;
+ esac
+ done # for ac_tag
+
+diff -Naur rt-3.6.10.orig/etc/upgrade/vulnerable-passwords.in rt-3.6.10/etc/upgrade/vulnerable-passwords.in
+--- rt-3.6.10.orig/etc/upgrade/vulnerable-passwords.in 1970-01-01 01:00:00.000000000 +0100
++++ rt-3.6.10/etc/upgrade/vulnerable-passwords.in 2011-01-27 00:15:25.000000000 +0100
+@@ -0,0 +1,93 @@
++#!@PERL@
++
++use strict;
++use warnings;
++
++use lib "@LOCAL_LIB_PATH@";
++use lib "@RT_LIB_PATH@";
++
++use RT;
++RT::LoadConfig;
++RT::Init;
++
++$| = 1;
++
++use Getopt::Long;
++use Digest::SHA;
++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", {
++ field => 'LENGTH(main.Password)',
++ op => '<',
++ value => '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;
++}
+diff -Naur rt-3.6.10.orig/lib/RT/User_Overlay.pm rt-3.6.10/lib/RT/User_Overlay.pm
+--- rt-3.6.10.orig/lib/RT/User_Overlay.pm 2009-11-30 19:45:26.000000000 +0100
++++ rt-3.6.10/lib/RT/User_Overlay.pm 2011-01-27 00:15:25.000000000 +0100
+@@ -77,6 +77,7 @@
+
+ %_USERS_KEY_CACHE = ();
+
++use Digest::SHA;
+ use Digest::MD5;
+ use RT::Principals;
+ use RT::ACE;
+@@ -1053,19 +1054,29 @@
+
+ }
+
+-=head2 _GeneratePassword PASSWORD
++=head2 _GeneratePassword PASSWORD [, SALT]
+
+-returns an MD5 hash of the password passed in, in hexadecimal encoding.
++Returns a salted SHA-256 hash of the password passed in, in base64
++encoding.
+
+ =cut
+
+ 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
++ );
+
+ }
+
+@@ -1136,23 +1147,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($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_utf8($value)) eq $stored;
++ } elsif (length $stored == 22) {
++ # Base64 nonsalted-md5
++ return 0 unless Digest::MD5::md5_base64(encode_utf8($value)) eq $stored;
++ } elsif (length $stored == 13) {
++ # crypt() output
++ return 0 unless crypt(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;
+ }
+
+ # }}}
+diff -Naur rt-3.6.10.orig/sbin/rt-test-dependencies.in rt-3.6.10/sbin/rt-test-dependencies.in
+--- rt-3.6.10.orig/sbin/rt-test-dependencies.in 2009-11-30 19:45:26.000000000 +0100
++++ rt-3.6.10/sbin/rt-test-dependencies.in 2011-01-27 00:15:25.000000000 +0100
+@@ -173,6 +173,7 @@
+ $deps{'CORE'} = [ text_to_hash( << '.') ];
+ Digest::base
+ Digest::MD5 2.27
++Digest::SHA
+ DBI 1.37
+ Class::ReturnValue 0.40
+ Date::Format
+diff -Naur rt-3.6.10.orig/UPGRADING rt-3.6.10/UPGRADING
+--- rt-3.6.10.orig/UPGRADING 2009-11-30 19:45:26.000000000 +0100
++++ rt-3.6.10/UPGRADING 2011-01-27 00:17:19.000000000 +0100
+@@ -16,6 +16,18 @@
+
+ *******
+
++UPGRADING FROM 3.6.10 and earlier - Changes:
++
++Previous versions of RT used a password hashing scheme which was too
++easy to reverse, which could allow attackers with read access to the
++RT database to possibly compromise users' passwords. Even if RT does
++no password authentication itself, it may still store these weak
++password hashes -- using ExternalAuth does not guarantee that you are
++not vulnerable! To upgrade stored passwords to a stronger hash, run:
++
++ perl etc/upgrade/vulnerable-passwords
++
++
+ UPGRADING FROM 3.5.7 and earlier - Changes:
+
+ Scrips are now prepared and committed in order alphanumerically by description.
diff --git a/rt3.spec b/rt3.spec
index a842ec7..6f73833 100644
--- a/rt3.spec
+++ b/rt3.spec
@@ -13,7 +13,7 @@
Name: rt3
Version: 3.6.10
-Release: 1%{?dist}
+Release: 2%{?dist}
Summary: Request tracker 3
Group: Applications/Internet
@@ -27,6 +27,7 @@ Source5: rt3.logrotate.in
Patch0: rt-3.6.1-config.diff
Patch1: rt-3.4.1-I18N.diff
Patch2: rt-3.6.0-Makefile.diff
+Patch3: rt3-3.6.10-salted_passwords.patch
BuildArch: noarch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
@@ -102,6 +103,7 @@ find etc -type f -exec chmod a-x {} \;
%patch0 -p1
%patch1 -p1
%patch2 -p1
+%patch3 -p1
# Patch backups added by rpm disturb
find -name '*.orig' -exec rm -f {} \;
@@ -186,6 +188,7 @@ rm -rf ${RPM_BUILD_ROOT}%{RT3_LIBDIR}/t
# Bogus
rm -f ${RPM_BUILD_ROOT}%{RT3_LIBDIR}/RT.pm.in
+rm -f ${RPM_BUILD_ROOT}%{_sysconfdir}/rt3/upgrade/vulnerable-passwords.in
# Unsupported
rm -f ${RPM_BUILD_ROOT}%{RT3_BINDIR}/*.scgi
@@ -280,6 +283,9 @@ fi
%{_mandir}/man1/rt-mailgate*
%changelog
+* Wed Jan 26 2011 Xavier Bachelot <xavier at bachelot.org> - 3.6.10-2
+- Add patch for CVE-2011-0009.
+
* Thu Dec 03 2009 Xavier Bachelot <xavier at bachelot.org> - 3.6.10-1
- Update to 3.6.10 for CVE-2009-3585 : session hijack.
More information about the scm-commits
mailing list