diff --git a/config.inc.php b/config.inc.php index 376bfbe3..949fe96e 100644 --- a/config.inc.php +++ b/config.inc.php @@ -194,6 +194,7 @@ $CONF['smtp_sendmail_tls'] = 'NO'; // - don't use dovecot:* methods that include the username in the hash - you won't be able to login to PostfixAdmin in this case // - you'll need at least dovecot 2.1 for salted passwords ('doveadm pw' 2.0.x doesn't support the '-t' option) // - dovecot 2.0.0 - 2.0.7 is not supported +// sha512.b64 - {SHA512-CRYPT.B64} (base64 encoded sha512) (no dovecot dependency; should support migration from md5crypt) $CONF['encrypt'] = 'md5crypt'; // In what flavor should courier-authlib style passwords be encrypted? diff --git a/functions.inc.php b/functions.inc.php index 457bb94e..4b2f9ac3 100644 --- a/functions.inc.php +++ b/functions.inc.php @@ -1232,6 +1232,8 @@ function pacrypt($pw, $pw_db="") { return _pacrypt_mysql_encrypt($pw, $pw_db); case 'authlib': return _pacrypt_authlib($pw, $pw_db); + case 'sha512.b64': + return _pacrypt_sha512_b64($pw, $pw_db); } if (preg_match("/^dovecot:/", $CONF['encrypt'])) { @@ -1245,6 +1247,35 @@ function pacrypt($pw, $pw_db="") { throw new Exception('unknown/invalid $CONF["encrypt"] setting: ' . $CONF['encrypt']); } +/** + * @see https://github.com/postfixadmin/postfixadmin/issues/58 + */ +function _pacrypt_sha512_b64($pw, $pw_db="") { + if (!function_exists('random_bytes') || !function_exists('crypt') || !defined('CRYPT_SHA512') || !function_exists('mb_substr')) { + throw new Exception("sha512.b64 not supported!"); + } + if (!$pw_db) { + $salt = mb_substr(rtrim(base64_encode(random_bytes(16)),'='),0,16,'8bit'); + return '{SHA512-CRYPT.B64}'.base64_encode(crypt($pw,'$6$'.$salt)); + } + + + $password="#Thepasswordcannotbeverified"; + if (strncmp($pw_db,'{SHA512-CRYPT.B64}',18)==0) { + $dcpwd = base64_decode(mb_substr($pw_db,18,NULL,'8bit'),true); + if ($dcpwd !== false && !empty($dcpwd) && strncmp($dcpwd,'$6$',3)==0) { + $password = '{SHA512-CRYPT.B64}'.base64_encode(crypt($pw,$dcpwd)); + } + } + elseif (strncmp($pw_db,'{MD5-CRYPT}',11)==0) { + $dcpwd = mb_substr($pw_db,11,NULL,'8bit'); + if (!empty($dcpwd) && strncmp($dcpwd,'$1$',3)==0) { + $password = '{MD5-CRYPT}'.crypt($pw,$dcpwd); + } + } + return $password; +} + /** * Creates MD5 based crypt formatted password. * If salt is not provided we generate one. diff --git a/tests/PacryptTest.php b/tests/PacryptTest.php index 24660706..6142439a 100644 --- a/tests/PacryptTest.php +++ b/tests/PacryptTest.php @@ -116,4 +116,29 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase { // not impossible though. $this->assertFalse( strcmp($str1, $str2) == 0 && strcmp($str1, $str3) == 0 ); } + + public function testSha512B64() { + $str1 = _pacrypt_sha512_b64('test', ''); + $str2 = _pacrypt_sha512_b64('test', ''); + + $this->assertNotEmpty($str1); + $this->assertNotEmpty($str2); + $this->assertNotEquals($str1, $str2); // should have different salts + + + $actualHash = '{SHA512-CRYPT.B64}JDYkM2NWcFM1WFNlUHl5MzdwSiRZWW80d0FmeWg5MXpxcS4uY3dtYUR1Y1RodTJGTDY1NHpXNUNvRU0wT3hXVFFzZkxIZ1JJSTZmT281OVpDUWJOTTF2L0JXajloME0vVjJNbENNMUdwLg=='; + + $check = _pacrypt_sha512_b64('test', $actualHash); + + $this->assertTrue(hash_equals($check, $actualHash)); + + $str3 = _pacrypt_sha512_b64('foo', ''); + + $this->assertNotEmpty($str3); + + $this->assertFalse(hash_equals('test', $str3)); + + $this->assertTrue(hash_equals(_pacrypt_sha512_b64('foo',$str3), $str3)); + + } }