From d6d9be4c6a751279c9ad2a9b50aeb5d3ff1f91f9 Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Sun, 15 Apr 2018 10:08:37 -0400 Subject: [PATCH 1/9] Fix variable syntax in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 47b6dd4b..f8c44b43 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ # Purpose of file: Global Makefile # ---------------------------------------------------------------------- MAJOR=$(shell sed -ne 's/^[^(]*(\([^)]*\)).*/\1/;1p' debian/changelog) -VERSION=$MAJOR +VERSION=$(MAJOR) export VERSION build: From bbb3e7c0e3f1f2f65ac9f18eb8fd5b512485e5bb Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Sun, 15 Apr 2018 18:25:58 -0400 Subject: [PATCH 2/9] Lengthen member's pass column to 255 characters --- install/mysql.sql | 2 +- install/upgrades/3.4.11.sql | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 install/upgrades/3.4.11.sql diff --git a/install/mysql.sql b/install/mysql.sql index 8a339b25..a41fa6f0 100644 --- a/install/mysql.sql +++ b/install/mysql.sql @@ -159,7 +159,7 @@ CREATE TABLE IF NOT EXISTS local ( CREATE TABLE IF NOT EXISTS membres ( uid int(10) unsigned NOT NULL auto_increment, -- Numéro du membre (GID) login varchar(128) NOT NULL default '', -- Nom d`utilisateur - pass varchar(64) NOT NULL default '', -- Mot de passe + pass varchar(255) NOT NULL default '', -- Mot de passe enabled tinyint(4) NOT NULL default '1', -- Le compte est-il actif ? su tinyint(4) NOT NULL default '0', -- Le compte est-il super-admin ? mail varchar(128) NOT NULL default '', -- Adresse email du possesseur diff --git a/install/upgrades/3.4.11.sql b/install/upgrades/3.4.11.sql new file mode 100644 index 00000000..7f922d97 --- /dev/null +++ b/install/upgrades/3.4.11.sql @@ -0,0 +1 @@ +ALTER TABLE `membres` MODIFY `pass` varchar(255); From 60846501813db924440985ea4f000574b19a5189 Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Sun, 15 Apr 2018 18:26:41 -0400 Subject: [PATCH 3/9] Use PHP's built-in password hashing and verification for user accounts --- bureau/class/functions.php | 13 +++++++++++++ bureau/class/m_admin.php | 4 ++-- bureau/class/m_mem.php | 6 +++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/bureau/class/functions.php b/bureau/class/functions.php index 0627744e..67b41ea8 100755 --- a/bureau/class/functions.php +++ b/bureau/class/functions.php @@ -541,6 +541,19 @@ function _md5cr($pass, $salt = "") { return crypt($pass, $salt); } +/** + * Transtional function to check if a string matches a saved password hash. + * @param string $pass string + * @param string $hash string + * @return bool + */ +function _password_verify($pass, $hash) { + if (strncmp($hash, '$1$', 3) == 0) { + // @TODO Raise a warning for the user to update their password. + return _md5cr($pass, $hash) == $hash; + } + return password_verify($pass, $hash); +} /** split mysql database name between username and custom database name * @param string $dbname database name diff --git a/bureau/class/m_admin.php b/bureau/class/m_admin.php index c16b8523..b2e16083 100644 --- a/bureau/class/m_admin.php +++ b/bureau/class/m_admin.php @@ -634,7 +634,7 @@ class m_admin { $msg->raise("ERROR", "admin", _("Login can only contains characters a-z, 0-9 and -")); return false; } - $pass = _md5cr($pass); + $pass = password_hash($pass); $db = new DB_System(); // Already exist? $db->query("SELECT count(*) AS cnt FROM membres WHERE login= ?;", array($login)); @@ -772,7 +772,7 @@ class m_admin { $db = new DB_System(); if ($pass) { - $pass = _md5cr($pass); + $pass = password_hash($pass); $second_query = "UPDATE membres SET mail= ?, canpass= ?, enabled= ?, `type`= ?, notes= ? , pass = ? WHERE uid= ?;"; $second_query_args = array($mail, $canpass, $enabled, $type, $notes, $pass, $uid); } else { diff --git a/bureau/class/m_mem.php b/bureau/class/m_mem.php index b5420b98..32ce6a55 100644 --- a/bureau/class/m_mem.php +++ b/bureau/class/m_mem.php @@ -93,7 +93,7 @@ class m_mem { return false; } $db->next_record(); - if (_md5cr($password, $db->f("pass")) != $db->f("pass")) { + if (!_password_verify($password, $db->f('pass'))) { $db->query("UPDATE membres SET lastfail=lastfail+1 WHERE uid= ? ;", array($db->f("uid"))); $msg->raise("ERROR", "mem", _("User or password incorrect")); return false; @@ -396,7 +396,7 @@ class m_mem { $msg->raise("ERROR", "mem", _("You are not allowed to change your password.")); return false; } - if ($this->user["pass"] != _md5cr($oldpass, $this->user["pass"])) { + if (!_password_verify($oldpass, $this->user['pass'])) { $msg->raise("ERROR", "mem", _("The old password is incorrect")); return false; } @@ -410,7 +410,7 @@ class m_mem { if (!$admin->checkPolicy("mem", $login, $newpass)) { return false; // The error has been raised by checkPolicy() } - $newpass = _md5cr($newpass); + $newpass = password_hash($newpass); $db->query("UPDATE membres SET pass= ? WHERE uid= ?;", array($newpass, $cuid)); $msg->init_msgs(); return true; From b5382bb13b11820d423f7c4144a77da4e75889db Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Sun, 15 Apr 2018 19:02:32 -0400 Subject: [PATCH 4/9] Replace _password_verify with password_verify PHP's password_verify function does know how to recognize md5 hashes: a custom check is not necessary. --- bureau/class/functions.php | 14 -------------- bureau/class/m_mem.php | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/bureau/class/functions.php b/bureau/class/functions.php index 67b41ea8..1e00cf36 100755 --- a/bureau/class/functions.php +++ b/bureau/class/functions.php @@ -541,20 +541,6 @@ function _md5cr($pass, $salt = "") { return crypt($pass, $salt); } -/** - * Transtional function to check if a string matches a saved password hash. - * @param string $pass string - * @param string $hash string - * @return bool - */ -function _password_verify($pass, $hash) { - if (strncmp($hash, '$1$', 3) == 0) { - // @TODO Raise a warning for the user to update their password. - return _md5cr($pass, $hash) == $hash; - } - return password_verify($pass, $hash); -} - /** split mysql database name between username and custom database name * @param string $dbname database name * @return array returns username as first element, custom name as second diff --git a/bureau/class/m_mem.php b/bureau/class/m_mem.php index 32ce6a55..7e31cbd5 100644 --- a/bureau/class/m_mem.php +++ b/bureau/class/m_mem.php @@ -93,7 +93,7 @@ class m_mem { return false; } $db->next_record(); - if (!_password_verify($password, $db->f('pass'))) { + if (!password_verify($password, $db->f('pass'))) { $db->query("UPDATE membres SET lastfail=lastfail+1 WHERE uid= ? ;", array($db->f("uid"))); $msg->raise("ERROR", "mem", _("User or password incorrect")); return false; @@ -396,7 +396,7 @@ class m_mem { $msg->raise("ERROR", "mem", _("You are not allowed to change your password.")); return false; } - if (!_password_verify($oldpass, $this->user['pass'])) { + if (!password_verify($oldpass, $this->user['pass'])) { $msg->raise("ERROR", "mem", _("The old password is incorrect")); return false; } From 971e38778f3d36414fcd1c26db07e36ec9fa7c9a Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Sun, 15 Apr 2018 19:03:57 -0400 Subject: [PATCH 5/9] Update stored password hash on user login. If an md5 hash is stored, a new hash will be calculated and stored. --- bureau/class/m_mem.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bureau/class/m_mem.php b/bureau/class/m_mem.php index 7e31cbd5..d4dce7af 100644 --- a/bureau/class/m_mem.php +++ b/bureau/class/m_mem.php @@ -104,6 +104,12 @@ class m_mem { } $this->user = $db->Record; $cuid = $db->f("uid"); + // Transitional code to update md5 hashed passwords to those created + // with password_hash(). + if (strncmp($db->f('pass'), '$1$', 3) == 0) { + $db->query("update membres set pass = ? where uid = ?", + array(password_hash($password), $cuid)); + } if (panel_islocked() && $cuid != 2000) { $msg->raise("ALERT", "mem", _("This website is currently under maintenance, login is currently disabled.")); From a609984d39a87dd89682422f6abeef7749251066 Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Sun, 15 Apr 2018 22:00:16 -0400 Subject: [PATCH 6/9] Fix invocations of password_hash() --- bureau/class/m_admin.php | 4 ++-- bureau/class/m_mem.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bureau/class/m_admin.php b/bureau/class/m_admin.php index b2e16083..cd9375db 100644 --- a/bureau/class/m_admin.php +++ b/bureau/class/m_admin.php @@ -634,7 +634,7 @@ class m_admin { $msg->raise("ERROR", "admin", _("Login can only contains characters a-z, 0-9 and -")); return false; } - $pass = password_hash($pass); + $pass = password_hash($pass, PASSWORD_BCRYPT); $db = new DB_System(); // Already exist? $db->query("SELECT count(*) AS cnt FROM membres WHERE login= ?;", array($login)); @@ -772,7 +772,7 @@ class m_admin { $db = new DB_System(); if ($pass) { - $pass = password_hash($pass); + $pass = password_hash($pass, PASSWORD_BCRYPT); $second_query = "UPDATE membres SET mail= ?, canpass= ?, enabled= ?, `type`= ?, notes= ? , pass = ? WHERE uid= ?;"; $second_query_args = array($mail, $canpass, $enabled, $type, $notes, $pass, $uid); } else { diff --git a/bureau/class/m_mem.php b/bureau/class/m_mem.php index d4dce7af..4885bed9 100644 --- a/bureau/class/m_mem.php +++ b/bureau/class/m_mem.php @@ -108,7 +108,7 @@ class m_mem { // with password_hash(). if (strncmp($db->f('pass'), '$1$', 3) == 0) { $db->query("update membres set pass = ? where uid = ?", - array(password_hash($password), $cuid)); + array(password_hash($password, PASSWORD_BCRYPT), $cuid)); } if (panel_islocked() && $cuid != 2000) { @@ -416,7 +416,7 @@ class m_mem { if (!$admin->checkPolicy("mem", $login, $newpass)) { return false; // The error has been raised by checkPolicy() } - $newpass = password_hash($newpass); + $newpass = password_hash($newpass, PASSWORD_BCRYPT); $db->query("UPDATE membres SET pass= ? WHERE uid= ?;", array($newpass, $cuid)); $msg->init_msgs(); return true; From 00c1d554060c13d0f251cf84b825f92caca2a407 Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Mon, 16 Apr 2018 21:02:45 -0400 Subject: [PATCH 7/9] Generate SHA512-CRYPT hashes for e-mail addresses --- bureau/class/functions.php | 29 +++++++++++++++++++++++++++++ bureau/class/m_mail.php | 6 ++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/bureau/class/functions.php b/bureau/class/functions.php index 0627744e..0b9ad41b 100755 --- a/bureau/class/functions.php +++ b/bureau/class/functions.php @@ -1204,3 +1204,32 @@ function csrf_check($token=null) { $db->exec("DELETE FROM csrf WHERE created= 7.0 + $salt = base64_encode(random_bytes(12)); + } + else if (function_exists('mcrypt_create_iv')) { + $salt = base64_encode(mcrypt_create_iv(12, MCRYPT_DEV_URANDOM)); + } + else if (function_exists('')) { + $salt = base64_encode(openssl_random_pseudo_bytes(12)); + } + if (!$salt) { + throw Error('Unable to generate salt'); + } + $salt = '$6$rounds=20000$' . $salt; + $hash = crypt($password, $salt); + // In any case the final password saved for dovecot can store the + // scheme to override the default on a per-account basis. + // Ideally this is updated to bcrypt or argon2 when those become + // available in dovecot. + // @see https://wiki.dovecot.org/Authentication/PasswordSchemes + return '{SHA512-CRYPT}' . $hash; +} diff --git a/bureau/class/m_mail.php b/bureau/class/m_mail.php index 28bcdb78..eec5c9a0 100644 --- a/bureau/class/m_mail.php +++ b/bureau/class/m_mail.php @@ -620,8 +620,10 @@ ORDER BY return false; } if ($canbeempty && empty($pass)) { - return $db->query("UPDATE address SET password= ? where id = ? ;", array(null, $mail_id )); - } else if (!$db->query("UPDATE address SET password= ? where id = ? ;", array(_md5cr($pass), $mail_id ))) { + return $db->query("UPDATE address SET password= ? where id = ? ;", + array(null, $mail_id )); + } else if (!$db->query("UPDATE address SET password= ? where id = ? ;", + array(_dovecot_hash($pass), $mail_id ))) { return false; } return true; From 56cbd2f8b4fc053b60fe76784f119a40a2e36f5d Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Mon, 16 Apr 2018 22:35:41 -0400 Subject: [PATCH 8/9] Move the sha512 crypt hash into it's own function --- bureau/class/functions.php | 40 ++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/bureau/class/functions.php b/bureau/class/functions.php index dea5fc5f..0fe4f259 100755 --- a/bureau/class/functions.php +++ b/bureau/class/functions.php @@ -1205,30 +1205,40 @@ function csrf_check($token=null) { } /** - * Create a password hash for use with dovecot. + * Create a SHA512-CRYPT hash of a string. */ -function _dovecot_hash($password) { - // Aim to have a 16 character salt for SHA-512 crypt. - // @see https://secure.php.net/manual/en/function.crypt.php - if (function_exists('random_bytes')) { - // PHP >= 7.0 - $salt = base64_encode(random_bytes(12)); - } - else if (function_exists('mcrypt_create_iv')) { - $salt = base64_encode(mcrypt_create_iv(12, MCRYPT_DEV_URANDOM)); - } - else if (function_exists('')) { - $salt = base64_encode(openssl_random_pseudo_bytes(12)); - } +function _sha512cr($password, $salt = NULL) { if (!$salt) { - throw Error('Unable to generate salt'); + // Aim to have a 16 character salt for SHA-512 crypt. + // @see https://secure.php.net/manual/en/function.crypt.php + if (function_exists('random_bytes')) { + // PHP >= 7.0 + $salt = base64_encode(random_bytes(12)); + } + else if (function_exists('mcrypt_create_iv')) { + $salt = base64_encode(mcrypt_create_iv(12, MCRYPT_DEV_URANDOM)); + } + else if (function_exists('')) { + $salt = base64_encode(openssl_random_pseudo_bytes(12)); + } + if (!$salt) { + throw Error('Unable to generate salt'); + } } $salt = '$6$rounds=20000$' . $salt; $hash = crypt($password, $salt); + return $hash; +} + +/** + * Create a password hash for use with dovecot. + */ +function _dovecot_hash($password) { // In any case the final password saved for dovecot can store the // scheme to override the default on a per-account basis. // Ideally this is updated to bcrypt or argon2 when those become // available in dovecot. // @see https://wiki.dovecot.org/Authentication/PasswordSchemes + $hash = _sha512cr($password); return '{SHA512-CRYPT}' . $hash; } From 88f34571911b109cf2224cfd84ffe413d0d32450 Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Mon, 16 Apr 2018 22:46:05 -0400 Subject: [PATCH 9/9] Use sha512 crypt to store ftp password hashes --- bureau/class/m_ftp.php | 4 ++-- install/mysql.sql | 2 +- install/upgrades/3.4.11.sql | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bureau/class/m_ftp.php b/bureau/class/m_ftp.php index 74b02788..11566a79 100644 --- a/bureau/class/m_ftp.php +++ b/bureau/class/m_ftp.php @@ -321,7 +321,7 @@ class m_ftp { return false; // The error has been raised by checkPolicy() } } - $encrypted_password = _md5cr($pass, strrev(microtime(true))); + $encrypted_password = _sha512cr($pass); $db->query("UPDATE ftpusers SET name= ? , password='', encrypted_password= ?, homedir= ?, uid= ? WHERE id= ?;", array($full_login, $encrypted_password, $absolute, $cuid, $id)); } else { $db->query("UPDATE ftpusers SET name= ? , homedir= ? , uid= ? WHERE id= ? ;", array($full_login, $absolute, $cuid, $id)); @@ -406,7 +406,7 @@ class m_ftp { } if ($quota->cancreate("ftp")) { - $encrypted_password = _md5cr($pass, strrev(microtime(true))); + $encrypted_password = _sha512cr($pass); $db->query("INSERT INTO ftpusers (name,password, encrypted_password,homedir,uid) VALUES ( ?, '', ?, ?, ?)", array($full_login, $encrypted_password, $absolute, $cuid)); return true; } else { diff --git a/install/mysql.sql b/install/mysql.sql index a41fa6f0..cc3f1d07 100644 --- a/install/mysql.sql +++ b/install/mysql.sql @@ -129,7 +129,7 @@ CREATE TABLE IF NOT EXISTS ftpusers ( id int(10) unsigned NOT NULL auto_increment, name varchar(64) NOT NULL default '', password varchar(32) NOT NULL default '', - encrypted_password VARCHAR(32) default NULL, + encrypted_password VARCHAR(255) default NULL, homedir varchar(128) NOT NULL default '', uid int(10) unsigned NOT NULL default '0', enabled boolean NOT NULL DEFAULT TRUE, diff --git a/install/upgrades/3.4.11.sql b/install/upgrades/3.4.11.sql index 7f922d97..643d3bc8 100644 --- a/install/upgrades/3.4.11.sql +++ b/install/upgrades/3.4.11.sql @@ -1 +1,2 @@ ALTER TABLE `membres` MODIFY `pass` varchar(255); +ALTER TABLE `ftpusers` MODIFY `encrypted_password` varchar(255);