From e9f843f29707f2f3c8f5c3792357d05e3ff64612 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Mon, 13 May 2019 14:29:24 -0400 Subject: [PATCH] bulk overlay hash mysql encoded passwords --- distros/debian/control | 6 ++++-- scripts/zmupdate.pl.in | 17 +++++++++++++---- web/includes/auth.php | 31 +++++++++++++++++++++---------- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/distros/debian/control b/distros/debian/control index dd5a405cd..59afe46a8 100644 --- a/distros/debian/control +++ b/distros/debian/control @@ -26,7 +26,7 @@ Build-Depends: debhelper (>= 9), cmake , libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl , libsys-cpu-perl, libsys-meminfo-perl , libdata-uuid-perl - , libssl-dev,libdigest-bcrypt-perl + , libdigest-bcrypt-perl, libdata-entropy-perl Standards-Version: 3.9.4 Package: zoneminder @@ -52,7 +52,9 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} , zip , libvlccore5 | libvlccore7 | libvlccore8, libvlc5 , libpolkit-gobject-1-0, php5-gd - , libssl,libdigest-bcrypt-perl + , libssl + ,libdigest-bcrypt-perl, libdata-entropy-perl + Recommends: mysql-server | mariadb-server Description: Video camera security and surveillance solution ZoneMinder is intended for use in single or multi-camera video security diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 136856d64..8c620443b 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -51,6 +51,8 @@ configuring upgrades etc, including on the fly upgrades. use strict; use bytes; use version; +use Digest; +use Data::Entropy::Algorithms qw(rand_bits); # ========================================================================== # @@ -1001,15 +1003,22 @@ sub patchDB { } sub migratePasswords { - - print ("****** MIGRATION"); + print ("Migratings passwords, if any...\n"); my $sql = "select * from Users"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); while( my $user = $sth->fetchrow_hashref() ) { - my $scheme = substr $user->{Password}, 0, 1; + my $scheme = substr($user->{Password}, 0, 1); if ($scheme eq "*") { - print ("*********** HOLY GODZILLA ".$user->{Username}. " uses PASSWORD"); + print ("-->".$user->{Username}. " password will be migrated\n"); + my $bcrypt = Digest->new('Bcrypt', cost=>10, salt=>rand_bits(16*8)); + my $settings = $bcrypt->settings(); + my $pass_hash = $bcrypt->add($user->{Password})->bcrypt_b64digest; + #print ("--- New pass overlay ----".$pass_hash); + my $new_pass_hash = "-ZM-".$settings.$pass_hash; + $sql = "UPDATE Users SET PASSWORD=? WHERE Username=?"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute($new_pass_hash, $user->{Username}) or die( "Can't execute: ".$sth->errstr() ); } } } diff --git a/web/includes/auth.php b/web/includes/auth.php index 643feb952..581fba1be 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -131,20 +131,31 @@ function userLogin($username='', $password='', $passwordHashed=false, $apiLogin $password_type = 'mysql'; } - else { - // bcrypt can have multiple signatures - if (preg_match('/^\$2[ayb]\$.+$/', $saved_password)) { + elseif (preg_match('/^\$2[ayb]\$.+$/', $saved_password)) { + ZM\Logger::Debug('bcrypt signature found, assumed bcrypt password'); + $password_type='bcrypt'; + $password_correct = $passwordHashed? ($password == $saved_password) : password_verify($password, $saved_password); + } + // zmupdate.pl adds a '-ZM-' prefix to overlay encrypted passwords + // this is done so that we don't spend cycles doing two bcrypt password_verify calls + // for every wrong password entered. This will only be invoked for passwords zmupdate.pl has + // overlay hashed + elseif (substr($saved_password, 0,4) == '-ZM-') { + ZM\Logger::Debug("Detected bcrypt overlay hashing for $username"); + ZM\Info("Detected bcrypt overlay hashing for $username"); + $bcrypt_hash = substr ($saved_password, 4); + $mysql_encoded_password ='*'.strtoupper(sha1(sha1($password, true))); + ZM\Logger::Debug("Comparing password $mysql_encoded_password to bcrypt hash: $bcrypt_hash"); + ZM\Info("Comparing password $mysql_encoded_password to bcrypt hash: $bcrypt_hash"); + $password_correct = password_verify($mysql_encoded_password, $bcrypt_hash); + $password_type = "mysql"; // so we can migrate later down - ZM\Logger::Debug ('bcrypt signature found, assumed bcrypt password'); - $password_type='bcrypt'; - $password_correct = $passwordHashed? ($password == $saved_password) : password_verify($password, $saved_password); - } - else { + } + else { // we really should nag the user not to use plain ZM\Warning ('assuming plain text password as signature is not known. Please do not use plain, it is very insecure'); $password_type = 'plain'; $password_correct = ($saved_password == $password); - } } } else { @@ -165,7 +176,7 @@ function userLogin($username='', $password='', $passwordHashed=false, $apiLogin ZM\Info("Login successful for user \"$username\""); $user = $saved_user_details; if ($password_type == 'mysql') { - ZM\Info ('Migrating password, if possible for future logins'); + ZM\Info('Migrating password, if possible for future logins'); migrateHash($username, $password); } unset($_SESSION['loginFailed']);