or return $e->die_event;
my $api = $self->api_name;
- # make sure the original password matches the in-database password
- if (md5_hex($orig_pw) ne $db_user->passwd) {
+ if (!$U->verify_migrated_user_password($e, $db_user->id, $orig_pw)) {
$e->rollback;
return new OpenILS::Event('INCORRECT_PASSWORD');
}
if( $api =~ /password/o ) {
+ # NOTE: with access to the plain text password we could crypt
+ # the password without the extra MD5 pre-hashing. Other changes
+ # would be required. Noting here for future reference.
+
+ # new password gets a new salt
+ my $new_salt = $e->json_query({
+ from => ['actor.create_salt', 'main']})->[0];
+ $new_salt = $new_salt->{'actor.create_salt'};
+
+ $e->json_query({
+ from => [
+ 'actor.set_passwd',
+ $db_user->id,
+ 'main',
+ md5_hex($new_salt . md5_hex($new_val)),
+ $new_salt
+ ]
+ });
- $db_user->passwd($new_val);
+ $db_user->passwd('');
} else {
return 0 if (!$user);
return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
- return 1 if $user->passwd eq $password;
- return 0;
+ return $U->verify_migrated_user_password(
+ $e, $user_by_username->id, $password, 1);
}
__PACKAGE__->register_method (
package OpenILS::Application::AppUtils;
-# vim:noet:ts=4
use strict; use warnings;
use OpenILS::Application;
use base qw/OpenILS::Application/;
use DateTime;
use DateTime::Format::ISO8601;
use List::MoreUtils qw/uniq/;
+use Digest::MD5 qw(md5_hex);
# ---------------------------------------------------------------------------
# Pile of utilty methods used accross applications.
return $result / 100;
}
+# Non-migrated passwords can be verified directly in the DB
+# with any extra hashing.
+sub verify_user_password {
+ my ($class, $e, $user_id, $passwd, $pw_type) = @_;
+
+ $pw_type ||= 'main'; # primary login password
+
+ my $verify = $e->json_query({
+ from => [
+ 'actor.verify_passwd',
+ $user_id, $pw_type, $passwd
+ ]
+ })->[0];
+
+ return $class->is_true($verify->{'actor.verify_passwd'});
+}
+
+# Passwords migrated from the original MD5 scheme are passed through 2
+# extra layers of MD5 hashing for backwards compatibility with the
+# MD5 passwords of yore and the MD5-based chap-style authentication.
+# Passwords are stored in the DB like this:
+# CRYPT( MD5( pw_salt || MD5(real_password) ), pw_salt )
+#
+# If 'as_md5' is true, the password provided has already been
+# MD5 hashed.
+sub verify_migrated_user_password {
+ my ($class, $e, $user_id, $passwd, $as_md5) = @_;
+
+ # 'main' is the primary login password. This is the only password
+ # type that requires the additional MD5 hashing.
+ my $pw_type = 'main';
+
+ # Sometimes we have the bare password, sometimes the MD5 version.
+ my $md5_pass = $as_md5 ? $passwd : md5_hex($passwd);
+
+ my $salt = $e->json_query({
+ from => [
+ 'actor.get_salt',
+ $user_id,
+ $pw_type
+ ]
+ })->[0];
+
+ $salt = $salt->{'actor.get_salt'};
+
+ return $class->verify_user_password(
+ $e, $user_id, md5_hex($salt . $md5_pass), $pw_type);
+}
+
1;