diff --git a/lib/RT/User.pm b/lib/RT/User.pm index 8cf787caf84..7b89d2166a4 100644 --- a/lib/RT/User.pm +++ b/lib/RT/User.pm @@ -1122,7 +1122,7 @@ sub IsPassword { my $salt = substr($hash, 0, 4, ""); return 0 unless RT::Util::constant_time_eq( substr(Digest::SHA::sha256($salt . Digest::MD5::md5(Encode::encode( "UTF-8", $value))), 0, 26), - $hash + $hash, 1 ); } elsif (length $stored == 32) { # Hex nonsalted-md5 diff --git a/lib/RT/Util.pm b/lib/RT/Util.pm index 317a20834cc..06cd046bc1c 100644 --- a/lib/RT/Util.pm +++ b/lib/RT/Util.pm @@ -166,6 +166,9 @@ The two string arguments B be of equal length. If the lengths differ, this function will call C, as proceeding with execution would create a timing vulnerability. Length is defined by characters, not bytes. +Strings that should be treated as binary octets rather than Unicode text +should pass a true value for the binary flag. + This code has been tested to do what it claims. Do not change it without thorough statistical timing analysis to validate the changes. @@ -177,7 +180,7 @@ B =cut sub constant_time_eq { - my ($a, $b) = @_; + my ($a, $b, $binary) = @_; my $result = 0; @@ -191,9 +194,18 @@ sub constant_time_eq { my $a_char = substr($a, $i, 1); my $b_char = substr($b, $i, 1); - # encode() is set to die on malformed - my @a_octets = unpack("C*", encode('UTF-8', $a_char, Encode::FB_CROAK)); - my @b_octets = unpack("C*", encode('UTF-8', $b_char, Encode::FB_CROAK)); + my (@a_octets, @b_octets); + + if ($binary) { + @a_octets = ord($a_char); + @b_octets = ord($b_char); + } + else { + # encode() is set to die on malformed + @a_octets = unpack("C*", encode('UTF-8', $a_char, Encode::FB_CROAK)); + @b_octets = unpack("C*", encode('UTF-8', $b_char, Encode::FB_CROAK)); + } + die $generic_error if (scalar @a_octets) != (scalar @b_octets); for (my $j = 0; $j < scalar @a_octets; $j++) {