diff options
Diffstat (limited to 'Bugzilla')
-rw-r--r-- | Bugzilla/Install/Localconfig.pm | 13 | ||||
-rw-r--r-- | Bugzilla/Template.pm | 4 | ||||
-rw-r--r-- | Bugzilla/Token.pm | 54 |
3 files changed, 69 insertions, 2 deletions
diff --git a/Bugzilla/Install/Localconfig.pm b/Bugzilla/Install/Localconfig.pm index 45005f032..654b44b9f 100644 --- a/Bugzilla/Install/Localconfig.pm +++ b/Bugzilla/Install/Localconfig.pm @@ -32,6 +32,7 @@ use strict; use Bugzilla::Constants; use Bugzilla::Install::Util qw(bin_loc); +use Bugzilla::Util qw(generate_random_password); use Data::Dumper; use File::Basename qw(dirname); @@ -185,6 +186,18 @@ EOT # Please specify the directory name only; do not use trailing slash. EOT }, + { + name => 'site_wide_secret', + default => generate_random_password(256), + desc => <<EOT +# This secret key is used by your installation for the creation and +# validation of encrypted tokens to prevent unsolicited changes, +# such as bug changes. A random string is generated by default. +# It's very important that this key is kept secret. It also must be +# very long. + +EOT + }, ); sub read_localconfig { diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 688c53386..8c34bb493 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -41,6 +41,7 @@ use Bugzilla::Util; use Bugzilla::User; use Bugzilla::Error; use Bugzilla::Status; +use Bugzilla::Token; use Bugzilla::Template::Parser; use Cwd qw(abs_path); @@ -765,6 +766,9 @@ sub create { return $docs_urlbase; }, + # Allow templates to generate a token themselves. + 'issue_hash_token' => \&Bugzilla::Token::issue_hash_token, + # These don't work as normal constants. DB_MODULE => \&Bugzilla::Constants::DB_MODULE, REQUIRED_MODULES => diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index 313d43212..f87490db1 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -39,10 +39,12 @@ use Bugzilla::User; use Date::Format; use Date::Parse; use File::Basename; +use Digest::MD5 qw(md5_hex); use base qw(Exporter); -@Bugzilla::Token::EXPORT = qw(issue_session_token check_token_data delete_token); +@Bugzilla::Token::EXPORT = qw(issue_session_token check_token_data delete_token + issue_hash_token check_hash_token); ################################################################################ # Public Functions @@ -170,6 +172,53 @@ sub issue_session_token { return _create_token(Bugzilla->user->id, 'session', $data); } +sub issue_hash_token { + my ($data, $time) = @_; + $data ||= []; + $time ||= time(); + + # The concatenated string is of the form + # token creation time + site-wide secret + user ID + data + my @args = ($time, Bugzilla->localconfig->{'site_wide_secret'}, Bugzilla->user->id, @$data); + my $token = md5_hex(join('*', @args)); + + # Prepend the token creation time, unencrypted, so that the token + # lifetime can be validated. + return $time . '-' . $token; +} + +sub check_hash_token { + my ($token, $data) = @_; + $data ||= []; + my ($time, $expected_token); + + if ($token) { + ($time, undef) = split(/-/, $token); + # Regenerate the token based on the information we have. + $expected_token = issue_hash_token($data, $time); + } + + if (!$token + || $expected_token ne $token + || time() - $time > MAX_TOKEN_AGE * 86400) + { + my $template = Bugzilla->template; + my $vars = {}; + $vars->{'script_name'} = basename($0); + $vars->{'token'} = issue_hash_token($data); + $vars->{'reason'} = (!$token) ? 'missing_token' : + ($expected_token ne $token) ? 'invalid_token' : + 'expired_token'; + print Bugzilla->cgi->header(); + $template->process('global/confirm-action.html.tmpl', $vars) + || ThrowTemplateError($template->error()); + exit; + } + + # If we come here, then the token is valid and not too old. + return 1; +} + sub CleanTokenTable { my $dbh = Bugzilla->dbh; $dbh->do('DELETE FROM tokens @@ -310,7 +359,7 @@ sub delete_token { # Note: this routine must not be called while tables are locked as it will try # to lock some tables itself, see CleanTokenTable(). sub check_token_data { - my ($token, $expected_action) = @_; + my ($token, $expected_action, $alternate_script) = @_; my $user = Bugzilla->user; my $template = Bugzilla->template; my $cgi = Bugzilla->cgi; @@ -330,6 +379,7 @@ sub check_token_data { $vars->{'token_action'} = $token_action; $vars->{'expected_action'} = $expected_action; $vars->{'script_name'} = basename($0); + $vars->{'alternate_script'} = $alternate_script || basename($0); # Now is a good time to remove old tokens from the DB. CleanTokenTable(); |