Commit 78a79955 authored by Max Kanat-Alexander's avatar Max Kanat-Alexander

Bug 658407: Make Bugzilla not use Math::Random::Secure anymore, due to the

difficulty of installing its dependencies. Instead move the code directly into Bugzilla itself. r=LpSolit, r=glob, a=mkanat
parent af8e935a
......@@ -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 };
......
......@@ -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;
......
......@@ -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;
......
# -*- 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;
......@@ -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;
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment