Commit d7ca3ba7 authored by Mic Kaczmarczik's avatar Mic Kaczmarczik
Browse files

Refactor to support multiple hashes at once and gradual upgrading of hashes as users log in

parent 3affa32b
......@@ -147,11 +147,11 @@ sub authentication {
## the user passwords
## Other backends are Single Sign-On solutions
if ($auth_service->{'auth_type'} eq 'user_table') {
# supply old password hash in case the hash uses a salt
my $fingerprint =
Sympa::User::password_fingerprint($pwd, $user->{'password'});
if ($fingerprint eq $user->{'password'}) {
Sympa::User::update_password_hash($user, $pwd);
Sympa::User::update_global_user($email,
{wrong_login_count => 0});
return {
......
......@@ -1647,6 +1647,14 @@ our @params = (
'gettext_comment' =>
"\"md5\" or \"bcrypt\".\nIf set to \"md5\", Sympa will use MD5 password hashes. If set to \"bcrypt\", bcrypt hashes will be used instead. This only concerns passwords stored in the Sympa database, not the ones in LDAP.\nShould not be changed! May invalid all user passwords.",
},
{ 'name' => 'password_hash_update',
'default' => '1',
'gettext_id' => 'Update password hashing algorithm when users log in',
'file' => 'wwsympa.conf',
#vhost => '1', # per-robot config is impossible.
'gettext_comment' =>
"On successful login, update the encrypted user password to use the algorithm specified by \"password_hash\". This allows for a graceful transition to a new password hash algorithm. A value of 0 disables updating of existing password hashes. New and reset passwords will use the \"password_hash\" setting in all cases.",
},
{ 'name' => 'bcrypt_cost',
'default' => '12',
'gettext_id' => 'Bcrypt hash cost',
......
......@@ -307,7 +307,12 @@ my %fingerprint_hashes = (
my ($pwd, $salt) = @_;
# salt parameter is not used for MD5 hashes
return Digest::MD5::md5_hex($pwd);
my $fingerprint = Digest::MD5::md5_hex($pwd);
my $match = ($fingerprint eq $salt) ? "yes" : "no";
$log->syslog('debug', "md5: match $match salt \"$salt\" fingerprint $fingerprint");
return $fingerprint;
},
# bcrypt uses a salt and has a configurable "cost" parameter
'bcrypt' => sub {
......@@ -344,20 +349,83 @@ my %fingerprint_hashes = (
sub password_fingerprint {
my ($pwd, $salt) = @_;
my $password_hash;
my $hash_type;
$log->syslog('debug', "password_fingerprint: salt $salt");
$log->syslog('debug', "salt \"$salt\"");
if (Conf::get_robot_conf('*', 'password_case') eq 'insensitive') {
$pwd = lc($pwd);
}
my $password_hash = Conf::get_robot_conf('*', 'password_hash');
# preserve the hash type if we can determine it, else use system default
if (defined($salt) && defined($hash_type = hash_type($salt))) {
$password_hash = $hash_type;
} else {
$password_hash = Conf::get_robot_conf('*', 'password_hash');
}
$log->syslog('debug', "hash_type \"$hash_type\", password_hash = \"$password_hash\"");
die "password_fingerprint: unknown password_hash \"$password_hash\""
unless defined($fingerprint_hashes{$password_hash});
return $fingerprint_hashes{$password_hash}->($pwd, $salt);
}
=over 4
=item hash_type ( )
detect the type of password fingerprint used for a hashed password
Returns undef if no supported hash type is detected
=back
=cut
sub hash_type {
my $hash = shift;
return 'md5' if ($hash =~ /^[a-f0-9]{32}$/i);
return 'bcrypt' if ($hash =~ m#\A\$2(a?)\$([0-9]{2})\$([./A-Za-z0-9]{22})#);
return undef;
}
=over 4
=item update_password_hash ( )
If needed, update the hash used for the user's encrypted password entry
=back
=cut
sub update_password_hash {
my ($user, $pwd) = @_;
return unless (Conf::get_robot_conf('*', 'password_hash_update'));
# here if configured to check and update the password hash algorithm
my $user_hash = hash_type($user->{'password'});
my $system_hash = Conf::get_robot_conf('*', 'password_hash');
return if (defined($user_hash) && ($user_hash eq $system_hash));
# note that we directly use the callback for the hash type
# instead of using any other logic to determine which to call
$log->syslog('debug', 'update password hash for %s from %s to %s',
$user->{'email'}, $user_hash, $system_hash);
# note that we use the cleartext password here, not the hash
update_global_user($user->{'email'}, {password => $pwd});
}
############################################################################
## Old-style functions
############################################################################
......
Supports Markdown
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