mirror of
https://github.com/postfixadmin/postfixadmin.git
synced 2024-09-19 19:22:14 +02:00
changes to pacrypt to support a prefix like {SHA265-CRYPT} on a hash - @see https://github.com/postfixadmin/postfixadmin/issues/344
This commit is contained in:
parent
ea7ecb113d
commit
7090b5af75
@ -184,7 +184,7 @@ $CONF['smtp_sendmail_tls'] = 'NO';
|
||||
// mysql_encrypt = useful for PAM integration
|
||||
// authlib = support for courier-authlib style passwords - also set $CONF['authlib_default_flavor']
|
||||
// dovecot:CRYPT-METHOD = use dovecotpw -s 'CRYPT-METHOD'. Example: dovecot:CRAM-MD5
|
||||
// php_crypt:CRYPT-METHOD:DIFFICULTY = use PHP built in crypt()-function. Example: php_crypt:SHA512:50000
|
||||
// php_crypt:CRYPT-METHOD:DIFFICULTY:PREFIX = use PHP built in crypt()-function. Example: php_crypt:SHA512:50000
|
||||
// - php_crypt CRYPT-METHOD: Supported values are DES, MD5, BLOWFISH, SHA256, SHA512
|
||||
// - php_crypt DIFFICULTY: Larger value is more secure, but uses more CPU and time for each login.
|
||||
// - php_crypt DIFFICULTY: Set this according to your CPU processing power.
|
||||
@ -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
|
||||
// - php_crypt PREFIX: hash has specified prefix - example: php_crypt:SHA512::{SHA256-CRYPT}
|
||||
// sha512.b64 - {SHA512-CRYPT.B64} (base64 encoded sha512) (no dovecot dependency; should support migration from md5crypt)
|
||||
$CONF['encrypt'] = 'md5crypt';
|
||||
|
||||
|
@ -1078,12 +1078,15 @@ function _pacrypt_dovecot($pw, $pw_db = '') {
|
||||
/**
|
||||
* Supports DES, MD5, BLOWFISH, SHA256, SHA512 methods.
|
||||
*
|
||||
* Via config we support an optional prefix (e.g. if you need hashes to start with {SHA256-CRYPT} and optional rounds (hardness) setting.
|
||||
*
|
||||
* @param string $pw
|
||||
* @param string $pw_db (can be empty if setting a new password)
|
||||
* @return string crypt'ed password; if it matches $pw_db then $pw is the original password.
|
||||
*/
|
||||
function _pacrypt_php_crypt($pw, $pw_db) {
|
||||
global $CONF;
|
||||
function _pacrypt_php_crypt($pw, $pw_db)
|
||||
{
|
||||
$configEncrypt = Config::read_string('encrypt');
|
||||
|
||||
// use PHPs crypt(), which uses the system's crypt()
|
||||
// same algorithms as used in /etc/shadow
|
||||
@ -1091,31 +1094,48 @@ function _pacrypt_php_crypt($pw, $pw_db) {
|
||||
// the algorithm for a new hash is chosen by feeding a salt with correct magic to crypt()
|
||||
// set $CONF['encrypt'] to 'php_crypt' to use the default SHA512 crypt method
|
||||
// set $CONF['encrypt'] to 'php_crypt:METHOD' to use another method; methods supported: DES, MD5, BLOWFISH, SHA256, SHA512
|
||||
// set $CONF['encrypt'] to 'php_crypt:METHOD:difficulty' where difficulty is between 1000-999999999
|
||||
// set $CONF['encrypt'] to 'php_crypt:METHOD:difficulty:PREFIX' to prefix the hash with the {PREFIX} etc.
|
||||
// tested on linux
|
||||
|
||||
$prefix = '';
|
||||
|
||||
if (strlen($pw_db) > 0) {
|
||||
// existing pw provided. send entire password hash as salt for crypt() to figure out
|
||||
$salt = $pw_db;
|
||||
|
||||
// if there was a prefix in the password, use this (override anything given in the config).
|
||||
|
||||
if (preg_match('/^\{([-A-Z0-9]+)\}(.+)$/', $pw_db, $method_matches)) {
|
||||
$salt = $method_matches[2];
|
||||
$prefix = "{" . $method_matches[1] . "}";
|
||||
}
|
||||
|
||||
} else {
|
||||
$salt_method = 'SHA512'; // hopefully a reasonable default (better than MD5)
|
||||
$hash_difficulty = '';
|
||||
// no pw provided. create new password hash
|
||||
if (strpos($CONF['encrypt'], ':') !== false) {
|
||||
if (strpos($configEncrypt, ':') !== false) {
|
||||
// use specified hash method
|
||||
$split_method = explode(':', $CONF['encrypt']);
|
||||
$salt_method = $split_method[1];
|
||||
if (count($split_method) >= 3) {
|
||||
$hash_difficulty = $split_method[2];
|
||||
$spec = explode(':', $configEncrypt);
|
||||
$salt_method = $spec[1];
|
||||
if (isset($spec[2])) {
|
||||
$hash_difficulty = $spec[2];
|
||||
}
|
||||
if (isset($spec[3])) {
|
||||
$prefix = $spec[3]; // hopefully something like {SHA256-CRYPT}
|
||||
}
|
||||
}
|
||||
// create appropriate salt for selected hash method
|
||||
$salt = _php_crypt_generate_crypt_salt($salt_method, $hash_difficulty);
|
||||
}
|
||||
// send it to PHPs crypt()
|
||||
|
||||
$password = crypt($pw, $salt);
|
||||
return $password;
|
||||
|
||||
return "{$prefix}{$password}";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $hash_type must be one of: MD5, DES, BLOWFISH, SHA256 or SHA512 (default)
|
||||
* @param int hash difficulty
|
||||
|
@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testMd5Crypt() {
|
||||
class PaCryptTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testMd5Crypt()
|
||||
{
|
||||
$hash = _pacrypt_md5crypt('test', '');
|
||||
|
||||
$this->assertNotEmpty($hash);
|
||||
@ -10,7 +12,8 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals($hash, _pacrypt_md5crypt('test', $hash));
|
||||
}
|
||||
|
||||
public function testCrypt() {
|
||||
public function testCrypt()
|
||||
{
|
||||
|
||||
// E_NOTICE if we pass in '' for the salt
|
||||
$hash = _pacrypt_crypt('test', 'sa');
|
||||
@ -21,7 +24,8 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals($hash, _pacrypt_crypt('test', $hash));
|
||||
}
|
||||
|
||||
public function testMySQLEncrypt() {
|
||||
public function testMySQLEncrypt()
|
||||
{
|
||||
if (!db_mysql()) {
|
||||
$this->markTestSkipped('Not using MySQL');
|
||||
}
|
||||
@ -45,7 +49,8 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testAuthlib() {
|
||||
public function testAuthlib()
|
||||
{
|
||||
global $CONF;
|
||||
|
||||
// too many options!
|
||||
@ -66,7 +71,8 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public function testPacryptDovecot() {
|
||||
public function testPacryptDovecot()
|
||||
{
|
||||
global $CONF;
|
||||
if (!file_exists('/usr/bin/doveadm')) {
|
||||
$this->markTestSkipped("No /usr/bin/doveadm");
|
||||
@ -82,9 +88,8 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
$this->assertEquals($expected_hash, _pacrypt_dovecot('test', $expected_hash));
|
||||
}
|
||||
|
||||
public function testPhpCrypt() {
|
||||
global $CONF;
|
||||
|
||||
public function testPhpCrypt()
|
||||
{
|
||||
$config = Config::getInstance();
|
||||
Config::write('encrypt', 'php_crypt:MD5');
|
||||
|
||||
@ -99,11 +104,44 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
|
||||
$fail = _pacrypt_php_crypt('bar', $expected);
|
||||
|
||||
|
||||
$this->assertNotEquals($fail, $expected);
|
||||
}
|
||||
|
||||
public function testPhpCryptRandomString() {
|
||||
public function testPhpCryptHandlesPrefixAndOrRounds()
|
||||
{
|
||||
// try with 1000 rounds
|
||||
Config::write('encrypt', 'php_crypt:SHA256:1000');
|
||||
$password = 'hello';
|
||||
|
||||
$randomHash = '$5$VhqhhsXJtPFeBX9e$kz3/CMIEu80bKdtDAcISIrDfdwtc.ilR68Vb3hNhu/7';
|
||||
$randomHashWithPrefix = '{SHA256-CRYPT}' . $randomHash;
|
||||
|
||||
$new = _pacrypt_php_crypt($password, '');
|
||||
|
||||
$this->assertNotEquals($randomHash, $new); // salts should be different.
|
||||
|
||||
$enc = _pacrypt_php_crypt($password, $randomHash);
|
||||
$this->assertEquals($enc, $randomHash);
|
||||
|
||||
$this->assertEquals($randomHash, _pacrypt_php_crypt("hello", $randomHash));
|
||||
$this->assertEquals($randomHash, _pacrypt_crypt("hello", $randomHash));
|
||||
|
||||
Config::write('encrypt', 'php_crypt:SHA256::{SHA256-CRYPT}');
|
||||
|
||||
$enc = _pacrypt_php_crypt("hello", $randomHash);
|
||||
$this->assertEquals($randomHash, $enc); // we passed in something lacking the prefix, so we shouldn't have added it in.
|
||||
$this->assertTrue(hash_equals($randomHash, $enc));
|
||||
|
||||
// should cope with this :
|
||||
$enc = _pacrypt_php_crypt($password, '');
|
||||
|
||||
$this->assertEquals($enc, _pacrypt_php_crypt($password, $enc));
|
||||
|
||||
$this->assertRegExp('/^\{SHA256-CRYPT\}/', $enc);
|
||||
$this->assertGreaterThan(20, strlen($enc));
|
||||
}
|
||||
|
||||
public function testPhpCryptRandomString()
|
||||
{
|
||||
$str1 = _php_crypt_random_string('abcdefg123456789', 2);
|
||||
$str2 = _php_crypt_random_string('abcdefg123456789', 2);
|
||||
$str3 = _php_crypt_random_string('abcdefg123456789', 2);
|
||||
@ -114,10 +152,11 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
|
||||
// it should be difficult for us to get three salts of the same value back...
|
||||
// not impossible though.
|
||||
$this->assertFalse( strcmp($str1, $str2) == 0 && strcmp($str1, $str3) == 0 );
|
||||
$this->assertFalse(strcmp($str1, $str2) == 0 && strcmp($str1, $str3) == 0);
|
||||
}
|
||||
|
||||
public function testSha512B64() {
|
||||
public function testSha512B64()
|
||||
{
|
||||
$str1 = _pacrypt_sha512_b64('test', '');
|
||||
$str2 = _pacrypt_sha512_b64('test', '');
|
||||
|
||||
@ -138,6 +177,6 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase {
|
||||
|
||||
$this->assertFalse(hash_equals('test', $str3));
|
||||
|
||||
$this->assertTrue(hash_equals(_pacrypt_sha512_b64('foo',$str3), $str3));
|
||||
$this->assertTrue(hash_equals(_pacrypt_sha512_b64('foo', $str3), $str3));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user