mirror of
https://github.com/postfixadmin/postfixadmin.git
synced 2024-09-19 19:22:14 +02:00
add SSHA format, add some specific courier algorithms, try and partially update docs
This commit is contained in:
parent
858c0a0ecd
commit
543285a203
@ -6,6 +6,9 @@ They should not be stored in plain text.
|
||||
|
||||
Whatever format you choose will need to be supported by your IMAP server (and whatever provides SASL auth for Postfix)
|
||||
|
||||
If you can, use a format that includes a different salt per password (e.g. one of the crypt variants, like Blowfish (BLF-CRYPT) or Argon2I/Argon2ID).
|
||||
Try and avoid formats that are unsalted hashes (md5, SHA1) as these offer minimal protection in the event of a data leak.
|
||||
|
||||
## Configuration
|
||||
|
||||
See config.inc.php (or config.local.php) and look for
|
||||
@ -20,6 +23,49 @@ This document is probably not complete.
|
||||
|
||||
It possibly provides better documentation than was present before. This may not say much.
|
||||
|
||||
Supported hash formats include :
|
||||
|
||||
* MD5-CRYPT (aka MD5),
|
||||
* SHA1,
|
||||
* SHA1-CRYPT,
|
||||
* SSHA (4 char salted sha1),
|
||||
* BLF-CRYPT (Blowfish),
|
||||
* SHA512,
|
||||
* SHA512-CRYPT,
|
||||
* ARGON2I,
|
||||
* ARGON2ID,
|
||||
* SHA256,
|
||||
* SHA256-CRYPT,
|
||||
* PLAIN-MD5 (aka md5)
|
||||
* CRYPT
|
||||
|
||||
Historically PostfixAdmin has supported all dovecot algorithms (methods) by using the 'doveadm' system binary. As of XXXXXXX, we attempt to use a native/PHP implementation for a number of these to remove issues caused by use of proc_open / dovecot file permissions etc (see #379).
|
||||
|
||||
It's recommended you use the algorithm/mechanism from your MTA, and configure PostfixAdmin with the same value prefixed by the MTA name -
|
||||
|
||||
For example, if dovecot has `default_pass_scheme = SHA256` use `$CONF['encrypt'] = 'dovecot:SHA256'; ` in PostfixAdmin.
|
||||
|
||||
|
||||
| Dovecot pass scheme | PostfixAdmin `$CONF['encrypt']` setting |
|
||||
|------|------|
|
||||
| SHA256 | dovecot:SHA256 |
|
||||
| SHA256-CRYPT.B64 | dovecot:SHA256-CRYPT.B64 |
|
||||
| SHA256-CRYPT | dovecot:SHA256-CRYPT |
|
||||
| SHA512-CRYPT | dovecot:SHA512-CRYPT |
|
||||
| ARGON2I | dovecot:ARGON2I |
|
||||
| ARGON2ID | dovecot:ARGON2ID |
|
||||
|
||||
|
||||
|
||||
| Courier Example | PostfixAdmin |
|
||||
|-------|-------|
|
||||
| md5 | courier:md5 |
|
||||
| md5raw | courier:md5raw |
|
||||
| sha1 | courier:sha1 |
|
||||
| ssha | courier:ssha |
|
||||
| sha256 | courier:sha256 |
|
||||
|
||||
|
||||
### cleartext
|
||||
|
||||
No hashing. May be useful for debugging.
|
||||
@ -58,9 +104,9 @@ You should not use this (it does not offer a high level of security), but is pro
|
||||
|
||||
Uses PHP's crypt function.
|
||||
|
||||
Probably throws an E_NOTICE. Avoid?
|
||||
Probably throws an E_NOTICE.
|
||||
|
||||
example : `$1$tWgqTIuF$1HFciCXrhVpACGjBMxNr/0`
|
||||
Example : `$1$tWgqTIuF$1HFciCXrhVpACGjBMxNr/0`
|
||||
|
||||
### authlib
|
||||
|
||||
@ -86,16 +132,18 @@ Presumably weak.
|
||||
|
||||
Uses sha1, base64 encoded. Unsalted. Avoid.
|
||||
|
||||
### dovecot:CRYPT-METHOD
|
||||
### dovecot:METHOD
|
||||
|
||||
Uses dovecot binary to produce hash.
|
||||
May use dovecot binary to produce hash, if the format you request isn't in PFACrypt::DOVECOT_NATIVE
|
||||
|
||||
Pros -
|
||||
Using a format that PostfixAdmin doesn't support natively has the following pros/cons :
|
||||
|
||||
#### Pros
|
||||
|
||||
* Minimal dependency on PostfixAdmin / PHP code.
|
||||
* Hash should definitely work with dovecot!
|
||||
|
||||
Cons -
|
||||
#### Cons
|
||||
|
||||
* file permissions and/or execution of doveadm by the web server may be problematic.
|
||||
* requires: proc_open(...) - which might be blocked by e.g. safemode.
|
||||
|
@ -4,21 +4,9 @@ class PFACrypt
|
||||
{
|
||||
private $algorithm;
|
||||
|
||||
public function __construct(string $algorithm)
|
||||
{
|
||||
$this->algorithm = $algorithm;
|
||||
}
|
||||
|
||||
public function hash(string $pw, string $pw_db = ''): string
|
||||
{
|
||||
$algorithm = $this->algorithm;
|
||||
|
||||
// try and 'upgrade' some dovecot commands to use local algorithms (rather tnan a dependency on the dovecot binary).
|
||||
if (preg_match('/^dovecot:/', $algorithm)) {
|
||||
$tmp = preg_replace('/^dovecot:/', '', $algorithm);
|
||||
|
||||
$supported = [
|
||||
'SHA1', 'SHA1,HEX', 'SHA1.B64',
|
||||
const DOVECOT_NATIVE = [
|
||||
'SHA1', 'SHA1.HEX', 'SHA1.B64',
|
||||
'SSHA',
|
||||
'BLF-CRYPT', 'BLF-CRYPT.B64',
|
||||
'SHA512-CRYPT', 'SHA512-CRYPT.B64',
|
||||
'ARGON2I',
|
||||
@ -32,10 +20,22 @@ class PFACrypt
|
||||
'CRYPT',
|
||||
];
|
||||
|
||||
if (in_array($tmp, $supported)) {
|
||||
public function __construct(string $algorithm)
|
||||
{
|
||||
$this->algorithm = $algorithm;
|
||||
}
|
||||
|
||||
public function hash(string $pw, string $pw_db = ''): string
|
||||
{
|
||||
$algorithm = $this->algorithm;
|
||||
|
||||
// try and 'upgrade' some dovecot commands to use local algorithms (rather tnan a dependency on the dovecot binary).
|
||||
if (preg_match('/^dovecot:/', $algorithm)) {
|
||||
$tmp = preg_replace('/^dovecot:/', '', $algorithm);
|
||||
if (in_array($tmp, self::DOVECOT_NATIVE)) {
|
||||
$algorithm = $tmp;
|
||||
} else {
|
||||
error_log("Warning: using alogrithm that requires proc_open: $algorithm, consider using one of : " . implode(', ', $supported));
|
||||
error_log("Warning: using algorithm that requires proc_open: $algorithm, consider using one of : " . implode(', ', self::DOVECOT_NATIVE));
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,8 +62,13 @@ class PFACrypt
|
||||
case 'ARGON2ID.B64':
|
||||
return $this->argon2idCrypt($pw, $pw_db, $algorithm);
|
||||
|
||||
case 'SSHA':
|
||||
case 'courier:ssha':
|
||||
return $this->hashSha1Salted($pw, $pw_db);
|
||||
|
||||
case 'SHA256':
|
||||
return '{SHA256}' . base64_encode(hash('sha256', $pw, true));
|
||||
case 'courier:sha256':
|
||||
return $this->hashSha256($pw);
|
||||
|
||||
case 'SHA256-CRYPT':
|
||||
case 'SHA256-CRYPT.B64':
|
||||
@ -79,6 +84,12 @@ class PFACrypt
|
||||
case 'PLAIN-MD5':
|
||||
return $this->hashMd5($pw, $algorithm);
|
||||
|
||||
case 'courier:md5':
|
||||
return '{MD5}' . base64_encode(md5($pw, true));
|
||||
|
||||
case 'courier:md5raw':
|
||||
return '{MD5RAW}' . bin2hex(md5($pw, true));
|
||||
|
||||
case 'MD5':
|
||||
case 'MD5-CRYPT':
|
||||
return $this->cryptMd5($pw, $pw_db, $algorithm);
|
||||
@ -118,6 +129,7 @@ class PFACrypt
|
||||
case 'sha512crypt.b64':
|
||||
return $this->pacrypt_sha512crypt_b64($pw, $pw_db);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (preg_match("/^dovecot:/", $algorithm)) {
|
||||
@ -144,6 +156,16 @@ class PFACrypt
|
||||
return "{{$algorithm}}{$hash}";
|
||||
}
|
||||
|
||||
public function hashSha1Salted(string $pw, string $pw_db = ''): string
|
||||
{
|
||||
if (empty($pw_db)) {
|
||||
$salt = base64_encode(random_bytes(3)); // 4 char salt.
|
||||
} else {
|
||||
$salt = substr(base64_decode(substr($pw_db, 6)), 20);
|
||||
}
|
||||
return '{SSHA}' . base64_encode(sha1($pw . $salt, true) . $salt);
|
||||
}
|
||||
|
||||
public function hashSha512(string $pw, string $algorithm = 'SHA512')
|
||||
{
|
||||
$prefix = '{SHA512}';
|
||||
@ -160,10 +182,14 @@ class PFACrypt
|
||||
if ($algorithm == 'PLAIN-MD5') {
|
||||
return '{PLAIN-MD5}' . md5($pw);
|
||||
}
|
||||
|
||||
return md5($pw);
|
||||
}
|
||||
|
||||
public function hashSha256(string $pw): string
|
||||
{
|
||||
return '{SHA256}' . base64_encode(hash('sha256', $pw, true));
|
||||
}
|
||||
|
||||
public function cryptMd5(string $pw, string $pw_db = '', $algorithm = 'MD5-CRYPT')
|
||||
{
|
||||
if (!empty($pw_db)) {
|
||||
|
@ -1,11 +1,15 @@
|
||||
<?php
|
||||
|
||||
require_once(__DIR__ . '/../model/PFACrypt.php');
|
||||
|
||||
class PaCryptTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testMd5Crypt()
|
||||
{
|
||||
$hash = _pacrypt_md5crypt('test', '');
|
||||
|
||||
$h = new PFACrypt('MD5-CRYPT');
|
||||
|
||||
$this->assertNotEmpty($hash);
|
||||
$this->assertNotEquals('test', $hash);
|
||||
|
||||
@ -204,6 +208,7 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase
|
||||
"CRYPT": "{CRYPT}$2y$05$ORqzr0AagWr25v3ixHD5QuMXympIoNTbipEFZz6aAmovGNoij2vDO",
|
||||
"MD5-CRYPT": "{MD5-CRYPT}$1$AIjpWveQ$2s3eEAbZiqkJhMYUIVR240",
|
||||
"PLAIN-MD5": "{PLAIN-MD5}cc03e747a6afbbcbf8be7668acfebee5",
|
||||
"SSHA": "{SSHA}ZkqrSEAhvd0FTHaK1IxAQCRa5LWbxGQY",
|
||||
"PLAIN": "{PLAIN}test123",
|
||||
"CLEAR": "{CLEAR}test123",
|
||||
"CLEARTEXT": "{CLEARTEXT}test123",
|
||||
@ -235,4 +240,44 @@ EOF;
|
||||
$this->assertEquals($pfa_new_hash, $new_new, "Trying: $algorithm => gave: $new_new with $pfa_new_hash ... ");
|
||||
}
|
||||
}
|
||||
|
||||
public function testSomeCourierHashes()
|
||||
{
|
||||
global $CONF;
|
||||
|
||||
$options = [
|
||||
'courier:md5' => '{MD5}zAPnR6avu8v4vnZorP6+5Q==',
|
||||
'courier:md5raw' => '{MD5RAW}cc03e747a6afbbcbf8be7668acfebee5',
|
||||
'courier:ssha' => '{SSHA}pJTac1QSIHoi0qBPdqnBvgPdjfFtDRVY',
|
||||
'courier:sha256' => '{SHA256}7NcYcNGWMxapfjrDQIyYNa2M8PPBvHA1J8MCZVNPda4=',
|
||||
];
|
||||
|
||||
foreach ($options as $algorithm => $example_hash) {
|
||||
$CONF['encrypt'] = $algorithm;
|
||||
|
||||
$pacrypt_check = pacrypt('test123', $example_hash);
|
||||
$pacrypt_sanity = pacrypt('zzzzz', $example_hash);
|
||||
$pfa_new_hash = pacrypt('test123');
|
||||
|
||||
$this->assertNotEquals($pacrypt_sanity, $pfa_new_hash);
|
||||
$this->assertNotEquals($pacrypt_sanity, $example_hash);
|
||||
|
||||
$this->assertEquals($pacrypt_check, $example_hash, "Should match, algorithm: $algorithm generated:{$pacrypt_check} vs example:{$example_hash}");
|
||||
|
||||
$new = pacrypt('test123', $pfa_new_hash);
|
||||
|
||||
$this->assertEquals($pfa_new_hash, $new, "Trying: $algorithm => gave: $new with $pfa_new_hash");
|
||||
}
|
||||
}
|
||||
|
||||
public function testWeSupportWhatWeSayWeDo()
|
||||
{
|
||||
foreach (PFACrypt::DOVECOT_NATIVE as $algorithm) {
|
||||
$c = new PFACrypt($algorithm);
|
||||
$hash1 = $c->hash('test123');
|
||||
|
||||
$this->assertEquals($hash1, $c->hash('test123', $hash1));
|
||||
$this->assertNotEquals($hash1, $c->hash('9999test9999', $hash1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user