]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 658407: Make Bugzilla not use Math::Random::Secure anymore, due to the
authorMax Kanat-Alexander <mkanat@bugzilla.org>
Thu, 4 Aug 2011 23:05:11 +0000 (16:05 -0700)
committerMax Kanat-Alexander <mkanat@bugzilla.org>
Thu, 4 Aug 2011 23:05:11 +0000 (16:05 -0700)
difficulty of installing its dependencies. Instead move the code directly
into Bugzilla itself.
r=LpSolit, r=glob, a=mkanat

Bugzilla/Constants.pm
Bugzilla/Install/CPAN.pm
Bugzilla/Install/Requirements.pm
Bugzilla/RNG.pm [new file with mode: 0644]
Bugzilla/Util.pm
mod_perl.pl

index 32c74e920a9ea060155ba27980d50a370ed8bde8..12cbef3c4add4f38e35c11ea230636890870d2eb 100644 (file)
@@ -515,7 +515,7 @@ use constant DB_MODULE => {
 };
 
 # True if we're on Win32.
-use constant ON_WINDOWS => ($^O =~ /MSWin32/i);
+use constant ON_WINDOWS => ($^O =~ /MSWin32/i) ? 1 : 0;
 # True if we're using ActiveState Perl (as opposed to Strawberry) on Windows.
 use constant ON_ACTIVESTATE => eval { &Win32::BuildNumber };
 
index a3f91370249a74d4f940af68df50c3f8b05e7685..31bd7f88fb303ef66413fc64ac0a5398da79faf9 100644 (file)
@@ -71,13 +71,6 @@ use constant REQUIREMENTS => (
 # we make it a constant.
 use constant BZ_LIB => abs_path(bz_locations()->{ext_libpath});
 
-# These modules are problematic to install with "notest" (sometimes they
-# get installed when they shouldn't). So we always test their installation
-# and never ignore test failures.
-use constant ALWAYS_TEST => qw(
-    Math::Random::Secure
-);
-
 # CPAN requires nearly all of its parameters to be set, or it will start
 # asking questions to the user. We want to avoid that, so we have
 # defaults here for most of the required parameters we know about, in case
@@ -202,10 +195,7 @@ sub install_module {
     print install_string('install_module', 
               { module => $module_name, version => $version }) . "\n";
 
-    if (_always_test($name)) {
-        CPAN::Shell->install($name);
-    }
-    elsif ($test) {
+    if ($test) {
         CPAN::Shell->force('install', $name);
     }
     else {
@@ -220,11 +210,6 @@ sub install_module {
     $CPAN::Config->{makepl_arg} = $original_makepl;
 }
 
-sub _always_test {
-    my ($name) = @_;
-    return grep(lc($_) eq lc($name), ALWAYS_TEST) ? 1 : 0;
-}
-
 sub set_cpan_config {
     my $do_global = shift;
     my $bzlib = BZ_LIB;
index c22c2de342cff25d5ef177dda0d150af73378021..8825eb3a7426aaab522cdb5759bbe88d1d49c781 100644 (file)
@@ -160,13 +160,27 @@ sub REQUIRED_MODULES {
         version => 0.22,
     },
     {
-        package => 'Math-Random-Secure',
-        module  => 'Math::Random::Secure',
-        # This is the first version that installs properly on Windows.
-        version => '0.05',
+        package => 'Math-Random-ISAAC',
+        module  => 'Math::Random::ISAAC',
+        version => '1.0.1',
     },
     );
 
+    if (ON_WINDOWS) {
+        push(@modules, {
+            package => 'Win32',
+            module  => 'Win32',
+            # 0.35 fixes a memory leak in GetOSVersion, which we use.
+            version => 0.35,
+        }, 
+        {
+            package => 'Win32-API',
+            module  => 'Win32::API',
+            # 0.55 fixes a bug with char* that might affect Bugzilla::RNG.
+            version => '0.55',
+        });
+    }
+
     my $extra_modules = _get_extension_requirements('REQUIRED_MODULES');
     push(@modules, @$extra_modules);
     return \@modules;
@@ -351,16 +365,6 @@ sub OPTIONAL_MODULES {
     },
     );
 
-    if (ON_WINDOWS) {
-        # SizeLimit needs Win32::API to work on Windows.
-        push(@modules, {
-            package => 'Win32-API',
-            module  => 'Win32::API',
-            version => 0,
-            feature => ['mod_perl'],
-        });
-    }
-
     my $extra_modules = _get_extension_requirements('OPTIONAL_MODULES');
     push(@modules, @$extra_modules);
     return \@modules;
diff --git a/Bugzilla/RNG.pm b/Bugzilla/RNG.pm
new file mode 100644 (file)
index 0000000..caa63ba
--- /dev/null
@@ -0,0 +1,233 @@
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Bugzilla Bug Tracking System.
+#
+# The Initial Developer of the Original Code is Google Inc.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s): 
+#   Max Kanat-Alexander <mkanat@bugzilla.org>
+
+package Bugzilla::RNG;
+use strict;
+use base qw(Exporter);
+use Bugzilla::Constants qw(ON_WINDOWS);
+
+use IO::File;
+use Math::Random::ISAAC;
+use if ON_WINDOWS, 'Win32::API';
+
+our $RNG;
+our @EXPORT_OK = qw(rand srand irand);
+
+# ISAAC, a 32-bit generator, should only be capable of generating numbers
+# between 0 and 2^32 - 1. We want _to_float to generate numbers possibly
+# including 0, but always less than 1.0. Dividing the integer produced
+# by irand() by this number should do that exactly.
+use constant DIVIDE_BY => 2**32;
+
+# How many bytes of seed to read.
+use constant SEED_SIZE => 16; # 128 bits.
+
+#################
+# Windows Stuff #
+#################
+
+# The type of cryptographic service provider we want to use.
+# This doesn't really matter for our purposes, so we just pick
+# PROV_RSA_FULL, which seems reasonable. For more info, see
+# http://msdn.microsoft.com/en-us/library/aa380244(v=VS.85).aspx
+use constant PROV_RSA_FULL => 1;
+
+# Flags for CryptGenRandom:
+# Don't ever display a UI to the user, just fail if one would be needed.
+use constant CRYPT_SILENT => 64;
+# Don't require existing public/private keypairs.
+use constant CRYPT_VERIFYCONTEXT => 0xF0000000;
+
+# For some reason, BOOLEAN doesn't work properly as a return type with 
+# Win32::API.
+use constant RTLGENRANDOM_PROTO => <<END;
+INT SystemFunction036(
+  PVOID RandomBuffer,
+  ULONG RandomBufferLength
+)
+END
+
+#################
+# RNG Functions #
+#################
+
+sub rand (;$) {
+    my ($limit) = @_;
+    my $int = irand();
+    return _to_float($int, $limit);
+}
+
+sub irand (;$) {
+    my ($limit) = @_;
+    Bugzilla::RNG::srand() if !defined $RNG;
+    my $int = $RNG->irand();
+    if (defined $limit) {
+        # We can't just use the mod operator because it will bias
+        # our output. Search for "modulo bias" on the Internet for
+        # details. This is slower than mod(), but does not have a bias,
+        # as demonstrated by Math::Random::Secure's uniform.t test.
+        return int(_to_float($int, $limit));
+    }
+    return $int;
+}
+
+sub srand (;$) {
+    my ($value) = @_;
+    # Remove any RNG that might already have been made.
+    $RNG = undef;
+    my %args;
+    if (defined $value) {
+        $args{seed} = $value;
+    }
+    $RNG = _create_rng(\%args);
+}
+
+sub _to_float {
+    my ($integer, $limit) = @_;
+    $limit ||= 1;
+    return ($integer / DIVIDE_BY) * $limit;
+}
+
+##########################
+# Seed and PRNG Creation #
+##########################
+
+sub _create_rng {
+    my ($params) = @_;
+
+    if (!defined $params->{seed}) {
+        $params->{seed} = _get_seed();
+    }
+
+    _check_seed($params->{seed});
+
+    my @seed_ints = unpack('L*', $params->{seed});
+
+    my $rng = Math::Random::ISAAC->new(@seed_ints);
+
+    # It's faster to skip the frontend interface of Math::Random::ISAAC
+    # and just use the backend directly. However, in case the internal
+    # code of Math::Random::ISAAC changes at some point, we do make sure
+    # that the {backend} element actually exists first.
+    return $rng->{backend} ? $rng->{backend} : $rng;
+}
+
+sub _check_seed {
+    my ($seed) = @_;
+    if (length($seed) < 8) {
+        warn "Your seed is less than 8 bytes (64 bits). It could be"
+             . " easy to crack";
+    }
+    # If it looks like we were seeded with a 32-bit integer, warn the
+    # user that they are making a dangerous, easily-crackable mistake.
+    elsif (length($seed) <= 10 and $seed =~ /^\d+$/) {
+        warn "RNG seeded with a 32-bit integer, this is easy to crack";
+    }
+}
+
+sub _get_seed {
+    return _windows_seed() if ON_WINDOWS;
+
+    if (-r '/dev/urandom') {
+        return _read_seed_from('/dev/urandom');
+    }
+
+    return _read_seed_from('/dev/random');
+}
+
+sub _read_seed_from {
+    my ($from) = @_;
+
+    my $fh = IO::File->new($from, "r") or die "$from: $!";
+    my $buffer;
+    $fh->read($buffer, SEED_SIZE);
+    if (length($buffer) < SEED_SIZE) {
+        die "Could not read enough seed bytes from $from, got only " 
+            . length($buffer);
+    }
+    $fh->close;
+    return $buffer;
+}
+
+sub _windows_seed {
+    my ($major, $minor) = (Win32::GetOSVersion())[1,2];
+    if ($major < 5) {
+        die "Bugzilla does not support versions of Windows before"
+            . " Windows 2000";
+    }
+    # This means Windows 2000.
+    if ($major == 5 and $minor == 0) {
+        return _win2k_seed();
+    }
+
+    my $rtlgenrand = Win32::API->new('advapi32', RTLGENRANDOM_PROTO);
+    if (!defined $rtlgenrand) {
+        die "Could not import RtlGenRand: $^E";
+    }
+    my $buffer = chr(0) x SEED_SIZE;
+    my $result = $rtlgenrand->Call($buffer, SEED_SIZE);
+    if (!$result) {
+        die "RtlGenRand failed: $^E";
+    }
+    return $buffer;
+}
+
+sub _win2k_seed {
+    my $crypt_acquire = Win32::API->new(
+        "advapi32", 'CryptAcquireContext', 'PPPNN', 'I');
+    if (!defined $crypt_acquire) {
+        die "Could not import CryptAcquireContext: $^E";
+    }
+
+    my $crypt_release = Win32::API->new(
+        "advapi32", 'CryptReleaseContext', 'NN', 'I');
+    if (!defined $crypt_release) {
+        die "Could not import CryptReleaseContext: $^E";
+    }
+
+    my $crypt_gen_random = Win32::API->new(
+        "advapi32", 'CryptGenRandom', 'NNP', 'I');
+    if (!defined $crypt_gen_random) {
+        die "Could not import CryptGenRandom: $^E";
+    }
+
+    my $context = chr(0) x Win32::API::Type->sizeof('PULONG');
+    my $acquire_result = $crypt_acquire->Call(
+        $context, 0, 0, PROV_RSA_FULL, CRYPT_SILENT | CRYPT_VERIFYCONTEXT);
+    if (!defined $acquire_result) {
+        die "CryptAcquireContext failed: $^E";
+    }
+
+    my $pack_type = Win32::API::Type::packing('PULONG');
+    $context = unpack($pack_type, $context);
+
+    my $buffer = chr(0) x SEED_SIZE;
+    my $rand_result = $crypt_gen_random->Call($context, SEED_SIZE, $buffer);
+    my $rand_error = $^E;
+    # We don't check this if it fails, we don't care.
+    $crypt_release->Call($context, 0);
+    if (!defined $rand_result) {
+        die "CryptGenRandom failed: $rand_error";
+    }
+    return $buffer;
+}
+
+1;
index 4afdef1b4040230c92ef41dc62251a21a5034ccf..ac6848bfa9b69c292bd47baeb7773727549ae242 100644 (file)
@@ -47,6 +47,7 @@ use base qw(Exporter);
                              detect_encoding);
 
 use Bugzilla::Constants;
+use Bugzilla::RNG qw(irand);
 
 use Date::Parse;
 use Date::Format;
@@ -55,7 +56,6 @@ use DateTime::TimeZone;
 use Digest;
 use Email::Address;
 use List::Util qw(first);
-use Math::Random::Secure qw(irand);
 use Scalar::Util qw(tainted blessed);
 use Template::Filters;
 use Text::Wrap;
index 460e6216bb868b4fe289b1130de751b594f0018d..2f4016952b57a78e6bc42da4587a92fbb93c2055 100644 (file)
@@ -39,7 +39,6 @@ use Apache2::Log ();
 use Apache2::ServerUtil;
 use ModPerl::RegistryLoader ();
 use File::Basename ();
-use Math::Random::Secure;
 
 # This loads most of our modules.
 use Bugzilla ();
@@ -49,6 +48,7 @@ use Bugzilla::CGI ();
 use Bugzilla::Extension ();
 use Bugzilla::Install::Requirements ();
 use Bugzilla::Util ();
+use Bugzilla::RNG ();
 
 # Make warnings go to the virtual host's log and not the main
 # server log.
@@ -68,11 +68,11 @@ my $cgi_path = Bugzilla::Constants::bz_locations()->{'cgi_path'};
 my $server = Apache2::ServerUtil->server;
 my $conf = <<EOT;
 # Make sure each httpd child receives a different random seed (bug 476622).
-# Math::Random::Secure has one srand that needs to be called for
+# Bugzilla::RNG has one srand that needs to be called for
 # every process, and Perl has another. (Various Perl modules still use
-# the built-in rand(), even though we only use Math::Random::Secure in
-# Bugzilla itself, so we need to srand() both of them.)
-PerlChildInitHandler "sub { Math::Random::Secure::srand(); srand(); }"
+# the built-in rand(), even though we never use it in Bugzilla itself,
+# so we need to srand() both of them.)
+PerlChildInitHandler "sub { Bugzilla::RNG::srand(); srand(); }"
 <Directory "$cgi_path">
     AddHandler perl-script .cgi
     # No need to PerlModule these because they're already defined in mod_perl.pl