LP#1468422 Password verify and password update
authorBill Erickson <berickxx@gmail.com>
Mon, 23 Nov 2015 22:13:48 +0000 (17:13 -0500)
committerBill Erickson <berickxx@gmail.com>
Fri, 26 Feb 2016 15:07:41 +0000 (10:07 -0500)
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 <berickxx@gmail.com>
Signed-off-by: Dan Wells <dbw2@calvin.edu>
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm

index 281298a..8e46888 100644 (file)
@@ -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 (
index ab89ac4..1378a47 100644 (file)
@@ -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;