From 5ddb3f61b5cf9c97cf5b45c42209a1083e8efff8 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 23 Nov 2015 17:13:48 -0500 Subject: [PATCH] LP#1468422 Password verify and password update These API's now support new-style passwords: open-ils.actor.verify_user_password open-ils.actor.user.password.update Signed-off-by: Bill Erickson Signed-off-by: Dan Wells --- .../src/perlmods/lib/OpenILS/Application/Actor.pm | 27 +++++++++--- .../perlmods/lib/OpenILS/Application/AppUtils.pm | 51 +++++++++++++++++++++- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm index 281298a1e0..8e468888e7 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm @@ -1475,15 +1475,32 @@ sub update_passwd { 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 { @@ -3301,8 +3318,8 @@ sub verify_user_password { 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 ( diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm index ab89ac4ac2..1378a471ba 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm @@ -1,5 +1,4 @@ package OpenILS::Application::AppUtils; -# vim:noet:ts=4 use strict; use warnings; use OpenILS::Application; use base qw/OpenILS::Application/; @@ -18,6 +17,7 @@ use Encode; use DateTime; use DateTime::Format::ISO8601; use List::MoreUtils qw/uniq/; +use Digest::MD5 qw(md5_hex); # --------------------------------------------------------------------------- # Pile of utilty methods used accross applications. @@ -2291,6 +2291,55 @@ sub fpsum { 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; -- 2.11.0